Skip to content

Commit

Permalink
Merge pull request #8585 from gphargreaves/#8315/concat-non-empty-str…
Browse files Browse the repository at this point in the history
…ings

Add additional checks for concat of non-empty strings to return non-falsy
  • Loading branch information
orklah committed Oct 15, 2022
2 parents 7ec5ffb + b89ff32 commit dbb8815
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 3 deletions.
Expand Up @@ -199,6 +199,14 @@ public static function analyze(
$numeric_type
);

$right_is_numeric = UnionTypeComparator::isContainedBy(
$codebase,
$right_type,
$numeric_type
);

$has_numeric_type = $left_is_numeric || $right_is_numeric;

if ($left_is_numeric) {
$right_uint = Type::getPositiveInt();
$right_uint->addType(new TLiteralInt(0));
Expand Down Expand Up @@ -230,16 +238,23 @@ public static function analyze(
$non_empty_string = clone $numeric_type;
$non_empty_string->addType(new TNonEmptyString());

$has_non_empty = UnionTypeComparator::isContainedBy(
$left_non_empty = UnionTypeComparator::isContainedBy(
$codebase,
$left_type,
$non_empty_string
) || UnionTypeComparator::isContainedBy(
);

$right_non_empty = UnionTypeComparator::isContainedBy(
$codebase,
$right_type,
$non_empty_string
);

$has_non_empty = $left_non_empty || $right_non_empty;
$all_non_empty = $left_non_empty && $right_non_empty;

$has_numeric_and_non_empty = $has_numeric_type && $has_non_empty;

$all_literals = $left_type->allLiterals() && $right_type->allLiterals();

if ($has_non_empty) {
Expand All @@ -248,7 +263,8 @@ public static function analyze(
} elseif ($all_lowercase) {
$result_type = Type::getNonEmptyLowercaseString();
} else {
$result_type = Type::getNonEmptyString();
$result_type = $all_non_empty || $has_numeric_and_non_empty ?
Type::getNonFalsyString() : Type::getNonEmptyString();
}
} else {
if ($all_literals) {
Expand Down
8 changes: 8 additions & 0 deletions src/Psalm/Type.php
Expand Up @@ -34,6 +34,7 @@
use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TNonEmptyLowercaseString;
use Psalm\Type\Atomic\TNonEmptyString;
use Psalm\Type\Atomic\TNonFalsyString;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
use Psalm\Type\Atomic\TNumericString;
Expand Down Expand Up @@ -219,6 +220,13 @@ public static function getNonEmptyString(): Union
return new Union([$type]);
}

public static function getNonFalsyString(): Union
{
$type = new TNonFalsyString();

return new Union([$type]);
}

public static function getNumeric(): Union
{
$type = new TNumeric;
Expand Down
26 changes: 26 additions & 0 deletions tests/BinaryOperationTest.php
Expand Up @@ -830,6 +830,32 @@ function example(object $foo): string
return ($foo instanceof FooInterface ? $foo->toString() : null) ?? "Not a stringable foo";
}',
],
'concatNonEmptyReturnNonFalsyString' => [
'<?php
/** @var non-empty-string $s1 */
$s1 = "0";
/** @var non-empty-string $s1 */
$s2 = "0";
$a = $s1.$s2;',
'assertions' => [
'$a===' => 'non-falsy-string',
],
],
'concatNumericWithNonEmptyReturnNonFalsyString' => [
'<?php
/** @var numeric-string $s1 */
$s1 = "1";
/** @var non-empty-string $s2 */
$s2 = "0";
$a = $s1.$s2;
$b = $s2.$s1;',
'assertions' => [
'$a===' => 'non-falsy-string',
'$b===' => 'non-falsy-string',
],
],
];
}

Expand Down

0 comments on commit dbb8815

Please sign in to comment.