Skip to content

Commit

Permalink
Fix string concatenation with benevolent union type
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 11, 2024
1 parent 28c5729 commit 4a4c739
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
9 changes: 8 additions & 1 deletion src/Type/BenevolentUnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,18 @@ protected function unionTypes(callable $getType): Type
return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$resultTypes));
}

protected function pickFromTypes(callable $getValues): array
protected function pickFromTypes(
callable $getValues,
callable $criteria,
): array
{
$values = [];
foreach ($this->getTypes() as $type) {
$innerValues = $getValues($type);
if ($innerValues === [] && $criteria($type)) {
return [];
}

foreach ($innerValues as $innerType) {
$values[] = $innerType;
}
Expand Down
38 changes: 30 additions & 8 deletions src/Type/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,27 +124,42 @@ public function getReferencedClasses(): array

public function getObjectClassNames(): array
{
return array_values(array_unique($this->pickFromTypes(static fn (Type $type) => $type->getObjectClassNames())));
return array_values(array_unique($this->pickFromTypes(
static fn (Type $type) => $type->getObjectClassNames(),
static fn (Type $type) => $type->isObject()->yes(),
)));
}

public function getObjectClassReflections(): array
{
return $this->pickFromTypes(static fn (Type $type) => $type->getObjectClassReflections());
return $this->pickFromTypes(
static fn (Type $type) => $type->getObjectClassReflections(),
static fn (Type $type) => $type->isObject()->yes(),
);
}

public function getArrays(): array
{
return $this->pickFromTypes(static fn (Type $type) => $type->getArrays());
return $this->pickFromTypes(
static fn (Type $type) => $type->getArrays(),
static fn (Type $type) => $type->isArray()->yes(),
);
}

public function getConstantArrays(): array
{
return $this->pickFromTypes(static fn (Type $type) => $type->getConstantArrays());
return $this->pickFromTypes(
static fn (Type $type) => $type->getConstantArrays(),
static fn (Type $type) => $type->isArray()->yes(),
);
}

public function getConstantStrings(): array
{
return $this->pickFromTypes(static fn (Type $type) => $type->getConstantStrings());
return $this->pickFromTypes(
static fn (Type $type) => $type->getConstantStrings(),
static fn (Type $type) => $type->isString()->yes(),
);
}

public function accepts(Type $type, bool $strictTypes): TrinaryLogic
Expand Down Expand Up @@ -719,7 +734,10 @@ public function shuffleArray(): Type

public function getEnumCases(): array
{
return $this->pickFromTypes(static fn (Type $type) => $type->getEnumCases());
return $this->pickFromTypes(
static fn (Type $type) => $type->getEnumCases(),
static fn (Type $type) => $type->isObject()->yes(),
);
}

public function isCallable(): TrinaryLogic
Expand Down Expand Up @@ -1073,15 +1091,19 @@ protected function unionTypes(callable $getType): Type
*/
protected function pickTypes(callable $getTypes): array
{
return $this->pickFromTypes($getTypes);
return $this->pickFromTypes($getTypes, static fn () => false);
}

/**
* @template T
* @param callable(Type $type): list<T> $getValues
* @param callable(Type $type): bool $criteria
* @return list<T>
*/
protected function pickFromTypes(callable $getValues): array
protected function pickFromTypes(
callable $getValues,
callable $criteria,
): array
{
$values = [];
foreach ($this->types as $type) {
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6404.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6399.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4357.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10863.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5817.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/array-chunk.php');
Expand Down
26 changes: 26 additions & 0 deletions tests/PHPStan/Analyser/data/bug-10863.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Bug10863;

use function PHPStan\Testing\assertType;

class Foo
{

/**
* @param __benevolent<int|false> $b
*/
public function doFoo($b): void
{
assertType('non-falsy-string', '@' . $b);
}

/**
* @param int|false $b
*/
public function doFoo2($b): void
{
assertType('non-falsy-string', '@' . $b);
}

}

0 comments on commit 4a4c739

Please sign in to comment.