From f215ed28d05813a9648ebc89adda5ea76dfe8309 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Fri, 26 Nov 2021 05:59:06 +0200 Subject: [PATCH 1/2] Drop special handling of constant references on enums Internally enum cases are constants, and they should be resolved as such. Fixes vimeo/psalm#6994 --- .../Fetch/ClassConstFetchAnalyzer.php | 78 ++++++++----------- tests/EnumTest.php | 20 +++++ 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php index bd19e990d9f..d6de213554b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php @@ -210,53 +210,43 @@ public static function analyze( $const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name); - if ($const_class_storage->is_enum) { - if (isset($const_class_storage->enum_cases[$stmt->name->name])) { - $class_constant_type = new Type\Union([ - new Type\Atomic\TEnumCase($fq_class_name, $stmt->name->name) - ]); - } else { - $class_constant_type = null; - } + if ($fq_class_name === $context->self + || ( + $statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer && + $fq_class_name === $statements_analyzer->getSource()->getFQCLN() + ) + ) { + $class_visibility = \ReflectionProperty::IS_PRIVATE; + } elseif ($context->self && + ($codebase->classlikes->classExtends($context->self, $fq_class_name) + || $codebase->classlikes->classExtends($fq_class_name, $context->self)) + ) { + $class_visibility = \ReflectionProperty::IS_PROTECTED; } else { - if ($fq_class_name === $context->self - || ( - $statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer && - $fq_class_name === $statements_analyzer->getSource()->getFQCLN() - ) - ) { - $class_visibility = \ReflectionProperty::IS_PRIVATE; - } elseif ($context->self && - ($codebase->classlikes->classExtends($context->self, $fq_class_name) - || $codebase->classlikes->classExtends($fq_class_name, $context->self)) - ) { - $class_visibility = \ReflectionProperty::IS_PROTECTED; - } else { - $class_visibility = \ReflectionProperty::IS_PUBLIC; - } - - try { - $class_constant_type = $codebase->classlikes->getClassConstantType( - $fq_class_name, - $stmt->name->name, - $class_visibility, - $statements_analyzer - ); - } catch (\InvalidArgumentException $_) { - return true; - } catch (\Psalm\Exception\CircularReferenceException $e) { - if (IssueBuffer::accepts( - new CircularReference( - 'Constant ' . $const_id . ' contains a circular reference', - new CodeLocation($statements_analyzer->getSource(), $stmt) - ), - $statements_analyzer->getSuppressedIssues() - )) { - // fall through - } + $class_visibility = \ReflectionProperty::IS_PUBLIC; + } - return true; + try { + $class_constant_type = $codebase->classlikes->getClassConstantType( + $fq_class_name, + $stmt->name->name, + $class_visibility, + $statements_analyzer + ); + } catch (\InvalidArgumentException $_) { + return true; + } catch (\Psalm\Exception\CircularReferenceException $e) { + if (IssueBuffer::accepts( + new CircularReference( + 'Constant ' . $const_id . ' contains a circular reference', + new CodeLocation($statements_analyzer->getSource(), $stmt) + ), + $statements_analyzer->getSuppressedIssues() + )) { + // fall through } + + return true; } if (!$class_constant_type) { diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 14dbc37b5e7..90bf11bee1b 100644 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -174,6 +174,26 @@ public static function foo(self $i) : void {} [], '8.1', ], + 'wildcardConstantsOnEnum' => [ + ' [], + [], + '8.1', + ], 'EnumCaseInAttribute' => [ ' Date: Sat, 27 Nov 2021 04:32:32 +0200 Subject: [PATCH 2/2] Correctly process constant references on enum-valued variables --- .../Fetch/ClassConstFetchAnalyzer.php | 78 ++++++++----------- tests/EnumTest.php | 27 +++++++ 2 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php index d6de213554b..6fc70b7e3b0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php @@ -509,53 +509,43 @@ public static function analyze( $const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name); - if ($const_class_storage->is_enum) { - if (isset($const_class_storage->enum_cases[$stmt->name->name])) { - $class_constant_type = new Type\Union([ - new Type\Atomic\TEnumCase($fq_class_name, $stmt->name->name) - ]); - } else { - $class_constant_type = null; - } + if ($fq_class_name === $context->self + || ( + $statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer && + $fq_class_name === $statements_analyzer->getSource()->getFQCLN() + ) + ) { + $class_visibility = \ReflectionProperty::IS_PRIVATE; + } elseif ($context->self && + ($codebase->classlikes->classExtends($context->self, $fq_class_name) + || $codebase->classlikes->classExtends($fq_class_name, $context->self)) + ) { + $class_visibility = \ReflectionProperty::IS_PROTECTED; } else { - if ($fq_class_name === $context->self - || ( - $statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer && - $fq_class_name === $statements_analyzer->getSource()->getFQCLN() - ) - ) { - $class_visibility = \ReflectionProperty::IS_PRIVATE; - } elseif ($context->self && - ($codebase->classlikes->classExtends($context->self, $fq_class_name) - || $codebase->classlikes->classExtends($fq_class_name, $context->self)) - ) { - $class_visibility = \ReflectionProperty::IS_PROTECTED; - } else { - $class_visibility = \ReflectionProperty::IS_PUBLIC; - } - - try { - $class_constant_type = $codebase->classlikes->getClassConstantType( - $fq_class_name, - $stmt->name->name, - $class_visibility, - $statements_analyzer - ); - } catch (\InvalidArgumentException $_) { - return true; - } catch (\Psalm\Exception\CircularReferenceException $e) { - if (IssueBuffer::accepts( - new CircularReference( - 'Constant ' . $const_id . ' contains a circular reference', - new CodeLocation($statements_analyzer->getSource(), $stmt) - ), - $statements_analyzer->getSuppressedIssues() - )) { - // fall through - } + $class_visibility = \ReflectionProperty::IS_PUBLIC; + } - return true; + try { + $class_constant_type = $codebase->classlikes->getClassConstantType( + $fq_class_name, + $stmt->name->name, + $class_visibility, + $statements_analyzer + ); + } catch (\InvalidArgumentException $_) { + return true; + } catch (\Psalm\Exception\CircularReferenceException $e) { + if (IssueBuffer::accepts( + new CircularReference( + 'Constant ' . $const_id . ' contains a circular reference', + new CodeLocation($statements_analyzer->getSource(), $stmt) + ), + $statements_analyzer->getSuppressedIssues() + )) { + // fall through } + + return true; } if (!$class_constant_type) { diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 90bf11bee1b..20ab198f545 100644 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -194,6 +194,33 @@ public static function foo(int $i) : void {} [], '8.1', ], + 'constantOfAVariableEnumClassString' => [ + ' [ + '$_z===' => '3', + ], + [], + '8.1', + ], + 'constantOfAVariableEnumInstance' => [ + ' [ + '$_z===' => '3', + ], + [], + '8.1', + ], 'EnumCaseInAttribute' => [ '