Repository: phpactor/phpactor (php)

Issue:
Trait method not recognized when applied to an Enum
Hello!

It seems that when a trait with a method is applied to an Enum, the method is not recognized as existing:

![image](https://user-images.githubusercontent.com/4176520/236446802-fec20aa8-0eeb-4d7c-a24c-c821188c6fa7.png)


Any thoughts?

Thanks!

Pull request description:
Gh 2256: Support traits in enums
Fixes #2256 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 56a7453a..97fc5b35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ Improvements:
 
   - Symfony: show and consider non-public services by default (e.g. in tests it's
     possible to retrieve non-public services) #2263
+  - Support traits in enums #2256
 
 Bug fixes:
 
diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php
index be0984ed..2e7e467f 100644
--- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php
+++ b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php
@@ -10,6 +10,7 @@ use Phpactor\WorseReflection\Core\ClassName;
 use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionEnumCaseCollection;
 use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionMethodCollection as CoreReflectionMethodCollection;
 use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionPropertyCollection as CoreReflectionPropertyCollection;
+use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionTraitCollection;
 use Phpactor\WorseReflection\Core\Reflection\ReflectionEnum as CoreReflectionEnum;
 use Phpactor\WorseReflection\Core\Reflection\ReflectionMember;
 use Phpactor\WorseReflection\Core\ServiceLocator;
@@ -22,6 +23,8 @@ use Phpactor\WorseReflection\Core\Util\NodeUtil;
 
 class ReflectionEnum extends AbstractReflectionClass implements CoreReflectionEnum
 {
+    private ?ReflectionTraitCollection $traits = null;
+
     public function __construct(
         private ServiceLocator $serviceLocator,
         private TextDocument $sourceCode,
@@ -47,6 +50,10 @@ class ReflectionEnum extends AbstractReflectionClass implements CoreReflectionEn
         $members = ClassLikeReflectionMemberCollection::empty();
         /** @phpstan-ignore-next-line Constants is compatible with this */
         $members = $members->merge($this->ownMembers());
+        foreach ($this->traits() as $trait) {
+            /** @phpstan-ignore-next-line Constants is compatible with this */
+            $members = $members->merge($trait->members());
+        }
         try {
             $enumType = $this->isBacked() ? 'BackedEnum' : 'UnitEnum';
             $enumMethods = $this->serviceLocator()->reflector()->reflectInterface($enumType)->members();
@@ -116,6 +123,19 @@ class ReflectionEnum extends AbstractReflectionClass implements CoreReflectionEn
         return 'enum';
     }
 
+    public function traits(): ReflectionTraitCollection
+    {
+        if ($this->traits) {
+            return $this->traits;
+        }
+
+        $traits = ReflectionTraitCollection::fromEnumDeclaration($this->serviceLocator, $this->node);
+
+        $this->traits = $traits;
+
+        return $traits;
+    }
+
     /**
      * @return EnumDeclaration
      */
diff --git a/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php b/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php
index 2d3a14f2..4f3265a2 100644
--- a/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php
+++ b/lib/WorseReflection/Core/Reflection/Collection/ReflectionTraitCollection.php
@@ -2,6 +2,7 @@
 
 namespace Phpactor\WorseReflection\Core\Reflection\Collection;
 
+use Microsoft\PhpParser\Node\Statement\EnumDeclaration;
 use Microsoft\PhpParser\Node\Statement\TraitDeclaration;
 use Phpactor\WorseReflection\Core\Exception\NotFound;
 use Phpactor\WorseReflection\Core\Reflection\ReflectionTrait;
@@ -62,4 +63,25 @@ class ReflectionTraitCollection extends AbstractReflectionCollection
 
         return new self($items);
     }
+
+    public static function fromEnumDeclaration(ServiceLocator $serviceLocator, EnumDeclaration $enumDeclaration): ReflectionTraitCollection
+    {
+        $items = [];
+        /** @phpstan-ignore-next-line not trusting TP */
+        foreach ($enumDeclaration?->enumMembers?->enumMemberDeclarations ?? [] as $memberDeclaration) {
+            if (false === $memberDeclaration instanceof TraitUseClause) {
+                continue;
+            }
+
+            foreach ($memberDeclaration->traitNameList->getValues() as $traitName) {
+                $traitName = TolerantQualifiedNameResolver::getResolvedName($traitName);
+                try {
+                    $items[(string) $traitName] = $serviceLocator->reflector()->reflectTrait(ClassName::fromString($traitName));
+                } catch (NotFound) {
+                }
+            }
+        }
+
+        return new self($items);
+    }
 }
diff --git a/lib/WorseReflection/Core/Reflection/ReflectionEnum.php b/lib/WorseReflection/Core/Reflection/ReflectionEnum.php
index 0abc333b..c91a6663 100644
--- a/lib/WorseReflection/Core/Reflection/ReflectionEnum.php
+++ b/lib/WorseReflection/Core/Reflection/ReflectionEnum.php
@@ -5,6 +5,7 @@ namespace Phpactor\WorseReflection\Core\Reflection;
 use Phpactor\WorseReflection\Core\DocBlock\DocBlock;
 use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionEnumCaseCollection;
 use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionPropertyCollection;
+use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionTraitCollection;
 use Phpactor\WorseReflection\Core\Type;
 
 interface ReflectionEnum extends ReflectionClassLike
@@ -18,4 +19,6 @@ interface ReflectionEnum extends ReflectionClassLike
     public function isBacked(): bool;
 
     public function backedType(): Type;
+
+    public function traits(): ReflectionTraitCollection;
 }


Test patch:
diff --git a/lib/WorseReflection/Tests/Inference/enum/enum_trait.test b/lib/WorseReflection/Tests/Inference/enum/enum_trait.test
new file mode 100644
index 00000000..af1828e2
--- /dev/null
+++ b/lib/WorseReflection/Tests/Inference/enum/enum_trait.test
@@ -0,0 +1,20 @@
+<?php
+
+namespace Phpactor\WorseReflection\Tests\Inference\reflection;
+
+trait enum_trait {
+    public function foo(): string
+    {
+        return 'bar';
+    }
+}
+
+
+enum ScheduleType: int
+{
+    use enum_trait;
+
+    public function label(): void {
+        wrAssertType('string', $this->foo());
+    }
+}