Skip to content

Commit 7cc7b73

Browse files
Fix union with empty string
1 parent b5fc9ec commit 7cc7b73

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

src/Type/TypeCombinator.php

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace PHPStan\Type;
44

55
use PHPStan\Type\Accessory\AccessoryArrayListType;
6+
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
67
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
78
use PHPStan\Type\Accessory\AccessoryType;
9+
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
810
use PHPStan\Type\Accessory\HasOffsetType;
911
use PHPStan\Type\Accessory\HasOffsetValueType;
1012
use PHPStan\Type\Accessory\HasPropertyType;
@@ -452,7 +454,10 @@ private static function compareTypesInUnion(Type $a, Type $b): ?array
452454
&& ($b->describe(VerbosityLevel::value()) === 'non-empty-string'
453455
|| $b->describe(VerbosityLevel::value()) === 'non-falsy-string')
454456
) {
455-
return [null, new StringType()];
457+
return [null, self::intersect(
458+
new StringType(),
459+
...self::getAccessoryCaseStringTypes($b),
460+
)];
456461
}
457462

458463
if (
@@ -461,34 +466,55 @@ private static function compareTypesInUnion(Type $a, Type $b): ?array
461466
&& ($a->describe(VerbosityLevel::value()) === 'non-empty-string'
462467
|| $a->describe(VerbosityLevel::value()) === 'non-falsy-string')
463468
) {
464-
return [new StringType(), null];
469+
return [self::intersect(
470+
new StringType(),
471+
...self::getAccessoryCaseStringTypes($a),
472+
), null];
465473
}
466474

467475
if (
468476
$a instanceof ConstantStringType
469477
&& $a->getValue() === '0'
470478
&& $b->describe(VerbosityLevel::value()) === 'non-falsy-string'
471479
) {
472-
return [null, new IntersectionType([
480+
return [null, self::intersect(
473481
new StringType(),
474482
new AccessoryNonEmptyStringType(),
475-
])];
483+
...self::getAccessoryCaseStringTypes($b),
484+
)];
476485
}
477486

478487
if (
479488
$b instanceof ConstantStringType
480489
&& $b->getValue() === '0'
481490
&& $a->describe(VerbosityLevel::value()) === 'non-falsy-string'
482491
) {
483-
return [new IntersectionType([
492+
return [self::intersect(
484493
new StringType(),
485494
new AccessoryNonEmptyStringType(),
486-
]), null];
495+
...self::getAccessoryCaseStringTypes($a),
496+
), null];
487497
}
488498

489499
return null;
490500
}
491501

502+
/**
503+
* @return array<Type>
504+
*/
505+
private static function getAccessoryCaseStringTypes(Type $type): array
506+
{
507+
$accessory = [];
508+
if ($type->isLowercaseString()->yes()) {
509+
$accessory[] = new AccessoryLowercaseStringType();
510+
}
511+
if ($type->isUppercaseString()->yes()) {
512+
$accessory[] = new AccessoryUppercaseStringType();
513+
}
514+
515+
return $accessory;
516+
}
517+
492518
private static function unionWithSubtractedType(
493519
Type $type,
494520
?Type $subtractedType,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12312;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @param lowercase-string $s
11+
*/
12+
public function sayLowercase(string $s): void
13+
{
14+
if ($s != '') {
15+
assertType('lowercase-string&non-empty-string', $s);
16+
}
17+
assertType('lowercase-string', $s);
18+
}
19+
20+
/**
21+
* @param uppercase-string $s
22+
*/
23+
public function sayUppercase(string $s): void
24+
{
25+
if ($s != '') {
26+
assertType('non-empty-string&uppercase-string', $s);
27+
}
28+
assertType('uppercase-string', $s);
29+
}
30+
31+
/**
32+
* @param lowercase-string&uppercase-string $s
33+
*/
34+
public function sayBoth(string $s): void
35+
{
36+
if ($s != '') {
37+
assertType('lowercase-string&non-empty-string&uppercase-string', $s);
38+
}
39+
assertType('lowercase-string&uppercase-string', $s);
40+
}
41+
}

0 commit comments

Comments
 (0)