mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 13:51:54 +01:00
Merge pull request #10545 from kkmuffme/basename-dirname-return-type-more-specific
make basename & dirname return types more specific
This commit is contained in:
commit
48ef3c474c
@ -44,7 +44,48 @@ final class BasenameReturnTypeProvider implements FunctionReturnTypeProviderInte
|
||||
);
|
||||
|
||||
if ($evaled_path === null) {
|
||||
return Type::getString();
|
||||
$union = $statements_source->getNodeTypeProvider()->getType($call_args[0]->value);
|
||||
$generic = false;
|
||||
$non_empty = false;
|
||||
if ($union !== null) {
|
||||
foreach ($union->getAtomicTypes() as $atomic) {
|
||||
if ($atomic instanceof Type\Atomic\TNonFalsyString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($atomic instanceof Type\Atomic\TLiteralString) {
|
||||
if ($atomic->value === '') {
|
||||
$generic = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($atomic->value === '0') {
|
||||
$non_empty = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($atomic instanceof Type\Atomic\TNonEmptyString) {
|
||||
$non_empty = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$generic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($union === null || $generic) {
|
||||
return Type::getString();
|
||||
}
|
||||
|
||||
if ($non_empty) {
|
||||
return Type::getNonEmptyString();
|
||||
}
|
||||
|
||||
return Type::getNonFalsyString();
|
||||
}
|
||||
|
||||
$basename = basename($evaled_path);
|
||||
|
@ -9,7 +9,6 @@ use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
|
||||
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TLiteralInt;
|
||||
use Psalm\Type\Atomic\TNonEmptyString;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
use function array_values;
|
||||
@ -39,6 +38,41 @@ final class DirnameReturnTypeProvider implements FunctionReturnTypeProviderInter
|
||||
$statements_source = $event->getStatementsSource();
|
||||
$node_type_provider = $statements_source->getNodeTypeProvider();
|
||||
|
||||
$union = $node_type_provider->getType($call_args[0]->value);
|
||||
$generic = false;
|
||||
if ($union !== null) {
|
||||
foreach ($union->getAtomicTypes() as $atomic) {
|
||||
if ($atomic instanceof Type\Atomic\TNonFalsyString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($atomic instanceof Type\Atomic\TLiteralString) {
|
||||
if ($atomic->value === '') {
|
||||
$generic = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 0 will be non-falsy too (.)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($atomic instanceof Type\Atomic\TNonEmptyString
|
||||
|| $atomic instanceof Type\Atomic\TEmptyNumeric) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// generic string is the only other possible case of empty string
|
||||
// which would result in a generic string
|
||||
$generic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$fallback_type = Type::getNonFalsyString();
|
||||
if ($union === null || $generic) {
|
||||
$fallback_type = Type::getString();
|
||||
}
|
||||
|
||||
$dir_level = 1;
|
||||
if (isset($call_args[1])) {
|
||||
$type = $node_type_provider->getType($call_args[1]->value);
|
||||
@ -49,7 +83,7 @@ final class DirnameReturnTypeProvider implements FunctionReturnTypeProviderInter
|
||||
$atomic_type->value > 0) {
|
||||
$dir_level = $atomic_type->value;
|
||||
} else {
|
||||
return Type::getString();
|
||||
return $fallback_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,17 +97,7 @@ final class DirnameReturnTypeProvider implements FunctionReturnTypeProviderInter
|
||||
);
|
||||
|
||||
if ($evaled_path === null) {
|
||||
$type = $node_type_provider->getType($call_args[0]->value);
|
||||
if ($type !== null && $type->isSingle()) {
|
||||
$atomic_type = array_values($type->getAtomicTypes())[0];
|
||||
if ($atomic_type instanceof TNonEmptyString) {
|
||||
return Type::getNonEmptyString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($evaled_path === null) {
|
||||
return Type::getString();
|
||||
return $fallback_type;
|
||||
}
|
||||
|
||||
$path_to_file = dirname($evaled_path, $dir_level);
|
||||
|
@ -32,5 +32,25 @@ class BasenameTest extends TestCase
|
||||
'$base===' => 'string',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'basenameOfStringPathReturnsNonEmptyString' => [
|
||||
'code' => '<?php
|
||||
$foo = rand(0, 1) ? "0" : "world";
|
||||
$base = basename($foo);
|
||||
',
|
||||
'assertions' => [
|
||||
'$base===' => 'non-empty-string',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'basenameOfStringPathReturnsNonFalsyString' => [
|
||||
'code' => '<?php
|
||||
$foo = rand(0, 1) ? "hello" : "world";
|
||||
$base = basename($foo);
|
||||
',
|
||||
'assertions' => [
|
||||
'$base===' => 'non-falsy-string',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,27 @@ class DirnameTest extends TestCase
|
||||
$dir = dirname(uniqid() . "abc", 2);
|
||||
',
|
||||
'assertions' => [
|
||||
'$dir===' => 'non-empty-string',
|
||||
'$dir===' => 'non-falsy-string',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'dirnameOfNonEmptyShouldBeNonFalsy' => [
|
||||
'code' => '<?php
|
||||
$foo = rand(0, 1) ? "0" : "world";
|
||||
$dir = dirname($foo, 20);
|
||||
',
|
||||
'assertions' => [
|
||||
'$dir===' => 'non-falsy-string',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'dirnameOfEmptyShouldBeString' => [
|
||||
'code' => '<?php
|
||||
$foo = rand(0, 1) ? "" : "world";
|
||||
$dir = dirname($foo, 20);
|
||||
',
|
||||
'assertions' => [
|
||||
'$dir===' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user