Skip to content

Commit 4a4c739

Browse files
committed
Fix string concatenation with benevolent union type
1 parent 28c5729 commit 4a4c739

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

src/Type/BenevolentUnionType.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,18 @@ protected function unionTypes(callable $getType): Type
4343
return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$resultTypes));
4444
}
4545

46-
protected function pickFromTypes(callable $getValues): array
46+
protected function pickFromTypes(
47+
callable $getValues,
48+
callable $criteria,
49+
): array
4750
{
4851
$values = [];
4952
foreach ($this->getTypes() as $type) {
5053
$innerValues = $getValues($type);
54+
if ($innerValues === [] && $criteria($type)) {
55+
return [];
56+
}
57+
5158
foreach ($innerValues as $innerType) {
5259
$values[] = $innerType;
5360
}

src/Type/UnionType.php

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,42 @@ public function getReferencedClasses(): array
124124

125125
public function getObjectClassNames(): array
126126
{
127-
return array_values(array_unique($this->pickFromTypes(static fn (Type $type) => $type->getObjectClassNames())));
127+
return array_values(array_unique($this->pickFromTypes(
128+
static fn (Type $type) => $type->getObjectClassNames(),
129+
static fn (Type $type) => $type->isObject()->yes(),
130+
)));
128131
}
129132

130133
public function getObjectClassReflections(): array
131134
{
132-
return $this->pickFromTypes(static fn (Type $type) => $type->getObjectClassReflections());
135+
return $this->pickFromTypes(
136+
static fn (Type $type) => $type->getObjectClassReflections(),
137+
static fn (Type $type) => $type->isObject()->yes(),
138+
);
133139
}
134140

135141
public function getArrays(): array
136142
{
137-
return $this->pickFromTypes(static fn (Type $type) => $type->getArrays());
143+
return $this->pickFromTypes(
144+
static fn (Type $type) => $type->getArrays(),
145+
static fn (Type $type) => $type->isArray()->yes(),
146+
);
138147
}
139148

140149
public function getConstantArrays(): array
141150
{
142-
return $this->pickFromTypes(static fn (Type $type) => $type->getConstantArrays());
151+
return $this->pickFromTypes(
152+
static fn (Type $type) => $type->getConstantArrays(),
153+
static fn (Type $type) => $type->isArray()->yes(),
154+
);
143155
}
144156

145157
public function getConstantStrings(): array
146158
{
147-
return $this->pickFromTypes(static fn (Type $type) => $type->getConstantStrings());
159+
return $this->pickFromTypes(
160+
static fn (Type $type) => $type->getConstantStrings(),
161+
static fn (Type $type) => $type->isString()->yes(),
162+
);
148163
}
149164

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

720735
public function getEnumCases(): array
721736
{
722-
return $this->pickFromTypes(static fn (Type $type) => $type->getEnumCases());
737+
return $this->pickFromTypes(
738+
static fn (Type $type) => $type->getEnumCases(),
739+
static fn (Type $type) => $type->isObject()->yes(),
740+
);
723741
}
724742

725743
public function isCallable(): TrinaryLogic
@@ -1073,15 +1091,19 @@ protected function unionTypes(callable $getType): Type
10731091
*/
10741092
protected function pickTypes(callable $getTypes): array
10751093
{
1076-
return $this->pickFromTypes($getTypes);
1094+
return $this->pickFromTypes($getTypes, static fn () => false);
10771095
}
10781096

10791097
/**
10801098
* @template T
10811099
* @param callable(Type $type): list<T> $getValues
1100+
* @param callable(Type $type): bool $criteria
10821101
* @return list<T>
10831102
*/
1084-
protected function pickFromTypes(callable $getValues): array
1103+
protected function pickFromTypes(
1104+
callable $getValues,
1105+
callable $criteria,
1106+
): array
10851107
{
10861108
$values = [];
10871109
foreach ($this->types as $type) {

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ public function dataFileAsserts(): iterable
764764
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6404.php');
765765
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6399.php');
766766
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4357.php');
767+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10863.php');
767768
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5817.php');
768769

769770
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-chunk.php');
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Bug10863;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param __benevolent<int|false> $b
12+
*/
13+
public function doFoo($b): void
14+
{
15+
assertType('non-falsy-string', '@' . $b);
16+
}
17+
18+
/**
19+
* @param int|false $b
20+
*/
21+
public function doFoo2($b): void
22+
{
23+
assertType('non-falsy-string', '@' . $b);
24+
}
25+
26+
}

0 commit comments

Comments
 (0)