1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Merge branch 'master' into fix-shepherd-without-custom-domain

This commit is contained in:
Alies Lapatsin 2023-01-22 13:03:40 +01:00 committed by GitHub
commit 73f90229bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 165 additions and 70 deletions

View File

@ -62,7 +62,9 @@ use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTemplateParam;
use Psalm\Type\Union;
use function array_filter;
use function array_keys;
use function array_map;
use function array_search;
use function count;
use function in_array;
@ -70,6 +72,8 @@ use function is_int;
use function is_string;
use function strtolower;
use const ARRAY_FILTER_USE_KEY;
/**
* @internal
*/
@ -198,7 +202,7 @@ class AtomicPropertyFetchAnalyzer
]),
);
} elseif ($prop_name === 'value' && $class_storage->enum_type !== null && $class_storage->enum_cases) {
self::handleEnumValue($statements_analyzer, $stmt, $class_storage);
self::handleEnumValue($statements_analyzer, $stmt, $stmt_var_type, $class_storage);
} elseif ($prop_name === 'name') {
self::handleEnumName($statements_analyzer, $stmt, $lhs_type_part);
} else {
@ -941,11 +945,31 @@ class AtomicPropertyFetchAnalyzer
private static function handleEnumValue(
StatementsAnalyzer $statements_analyzer,
PropertyFetch $stmt,
Union $stmt_var_type,
ClassLikeStorage $class_storage
): void {
$relevant_enum_cases = array_filter(
$stmt_var_type->getAtomicTypes(),
static fn(Atomic $type): bool => $type instanceof TEnumCase,
);
$relevant_enum_case_names = array_map(
static fn(TEnumCase $enumCase): string => $enumCase->case_name,
$relevant_enum_cases,
);
$enum_cases = $class_storage->enum_cases;
if (!empty($relevant_enum_case_names)) {
// If we have a known subset of enum cases, include only those
$enum_cases = array_filter(
$enum_cases,
static fn(string $key) => in_array($key, $relevant_enum_case_names, true),
ARRAY_FILTER_USE_KEY,
);
}
$case_values = [];
foreach ($class_storage->enum_cases as $enum_case) {
foreach ($enum_cases as $enum_case) {
if (is_string($enum_case->value)) {
$case_values[] = new TLiteralString($enum_case->value);
} elseif (is_int($enum_case->value)) {
@ -956,7 +980,6 @@ class AtomicPropertyFetchAnalyzer
}
}
// todo: this is suboptimal when we reference enum directly, e.g. Status::Open->value
/** @psalm-suppress ArgumentTypeCoercion */
$statements_analyzer->node_data->setType(
$stmt,

View File

@ -36,17 +36,19 @@ class DocCommentTest extends BaseTestCase
],
);
$expectedDoc = '/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
';
$expectedDoc = <<<'PHP'
/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
PHP;
$this->assertSame($expectedDoc, $docComment->render(''));
}
@ -74,15 +76,17 @@ class DocCommentTest extends BaseTestCase
],
);
$expectedDoc = '/**
* some desc
*
* @param string $bli
* @param int $bla
* @throws \Exception
* @return bool
*/
';
$expectedDoc = <<<'PHP'
/**
* some desc
*
* @param string $bli
* @param int $bla
* @throws \Exception
* @return bool
*/
PHP;
$this->assertSame($expectedDoc, $docComment->render(''));
}
@ -110,17 +114,19 @@ class DocCommentTest extends BaseTestCase
],
);
$expectedDoc = '/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
';
$expectedDoc = <<<'PHP'
/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
PHP;
$this->assertSame($expectedDoc, $docComment->render(''));
}
@ -129,17 +135,19 @@ class DocCommentTest extends BaseTestCase
{
ParsedDocblock::addNewLineBetweenAnnotations(true);
$expectedDoc = '/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
';
$expectedDoc = <<<'PHP'
/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
PHP;
$docComment = DocComment::parsePreservingLength(
new Doc($expectedDoc),
);
@ -151,17 +159,21 @@ class DocCommentTest extends BaseTestCase
{
ParsedDocblock::addNewLineBetweenAnnotations(true);
$expectedDoc = '/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
';
$expectedDoc = <<<'PHP'
/**
* some desc
*
* @param string $bli
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
PHP
. " ";
$docComment = DocComment::parsePreservingLength(
new Doc($expectedDoc),
);
@ -173,19 +185,21 @@ class DocCommentTest extends BaseTestCase
{
ParsedDocblock::addNewLineBetweenAnnotations(true);
$expectedDoc = '/**
* some self-referential desc with " * @return bool
* " as part of it.
*
* @param string $bli
* @param string $bli_this_suffix_is_kept
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
';
$expectedDoc = <<<'PHP'
/**
* some self-referential desc with " * @return bool
* " as part of it.
*
* @param string $bli
* @param string $bli_this_suffix_is_kept
* @param int $bla
*
* @throws \Exception
*
* @return bool
*/
PHP;
$docComment = DocComment::parsePreservingLength(
new Doc($expectedDoc),
);

View File

@ -94,12 +94,70 @@ class EnumTest extends TestCase
$z = Mask::Two->value;
',
'assertions' => [
// xxx: we should be able to do better when we reference a case explicitly, like above
'$z===' => '2',
],
'ignored_issues' => [],
'php_version' => '8.1',
],
'EnumCaseValue #8568' => [
'code' => '<?php
enum Mask: int {
case One = 1 << 0;
case Two = 1 << 1;
}
/** @return Mask */
function a() {
return Mask::One;
}
$z = a()->value;
',
'assertions' => [
'$z===' => '1|2',
],
'ignored_issues' => [],
'php_version' => '8.1',
],
'EnumUnionAsCaseValue #8568' => [
'code' => '<?php
enum Mask: int {
case One = 1 << 0;
case Two = 1 << 1;
case Four = 1 << 2;
}
/** @return Mask::One|Mask::Two */
function a() {
return Mask::One;
}
$z = a()->value;
',
'assertions' => [
'$z===' => '1|2',
],
'ignored_issues' => [],
'php_version' => '8.1',
],
'matchCaseOnEnumValue #8812' => [
'code' => '<?php
enum SomeType: string
{
case FOO = "FOO";
case BAR = "BAR";
}
function getSomething(string $moduleString): int
{
return match ($moduleString) {
SomeType::FOO->value => 1,
SomeType::BAR->value => 2,
};
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.1',
],
'namePropertyFromOutside' => [
'code' => '<?php
enum Status