diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php index 585143cc4c6..340125bcf7b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php @@ -218,12 +218,16 @@ public static function analyze( if ($class_storage->is_enum || in_array('UnitEnum', $codebase->getParentInterfaces($fq_class_name))) { if ($prop_name === 'value' && !$class_storage->is_enum) { $has_valid_fetch_type = true; + $stmt_type = $statements_analyzer->node_data->getType($stmt); $statements_analyzer->node_data->setType( $stmt, - new Union([ - new TString(), - new TInt(), - ]), + Type::combineUnionTypes( + new Union([ + new TString(), + new TInt(), + ]), + $stmt_type, + ), ); } elseif ($prop_name === 'value' && $class_storage->enum_type !== null && $class_storage->enum_cases) { $has_valid_fetch_type = true; @@ -1024,14 +1028,18 @@ private static function handleEnumName( $relevant_enum_case_names = array_keys($class_storage->enum_cases); } + $stmt_type = $statements_analyzer->node_data->getType($stmt); $statements_analyzer->node_data->setType( $stmt, - empty($relevant_enum_case_names) - ? Type::getNonEmptyString() - : new Union(array_map( - static fn(string $name): TString => Type::getAtomicStringFromLiteral($name), - $relevant_enum_case_names, - )), + Type::combineUnionTypes( + empty($relevant_enum_case_names) + ? Type::getNonEmptyString() + : new Union(array_map( + static fn(string $name): TString => Type::getAtomicStringFromLiteral($name), + $relevant_enum_case_names, + )), + $stmt_type, + ), ); } @@ -1067,10 +1075,14 @@ private static function handleEnumValue( $case_values[] = $case_value ?? new TMixed(); } + $stmt_type = $statements_analyzer->node_data->getType($stmt); /** @psalm-suppress ArgumentTypeCoercion */ $statements_analyzer->node_data->setType( $stmt, - new Union($case_values), + Type::combineUnionTypes( + new Union($case_values), + $stmt_type, + ), ); } diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 9999497c4ab..69584343ca2 100644 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -742,6 +742,51 @@ enum Foo: string { 'ignored_issues' => [], 'php_version' => '8.1', ], + 'nullCoalesceOnNullableBackedEnumValue #11171' => [ + 'code' => <<<'PHP' + value ?? "none"; + } + function nullsafe(?Suit $suit): string { + return $suit?->value ?? "none"; + } + PHP, + 'assertions' => [], + 'ignored_issues' => [], + 'php_version' => '8.1', + ], + 'nullCoalesceOnNullableEnumName #11171' => [ + 'code' => <<<'PHP' + name ?? "unknown"; + } + PHP, + 'assertions' => [], + 'ignored_issues' => [], + 'php_version' => '8.1', + ], + 'nullCoalesceOnNullableBackedEnumInterfaceValue #11171' => [ + 'code' => <<<'PHP' + value ?? "none"; + } + PHP, + 'assertions' => [], + 'ignored_issues' => [], + 'php_version' => '8.1', + ], ]; }