Repository: phpactor/phpactor (php)

Issue:
Method "cases" does not exist on class My\Beautiful\Enum
Hello,

I'm encountering an issue with enums `Method "cases" does not exist on class "xxxx".

![image](https://github.com/phpactor/phpactor/assets/5028767/5f3b1883-87b6-4ab8-ab9a-c94401eb8cb6)

I was thinking it could be mason related as it doesn't provide the latest version but after updating manually the problem persists.
I'm using the latest version avaiable
![image](https://github.com/phpactor/phpactor/assets/5028767/627573d4-3864-42ea-8473-351763a3666e)




Pull request description:
Gh 2378 enum cases
Fixes #2378 

Fix enum cases false alarm.

Was because of "contextual source location" which is enabled by default but disabled in the LS process, but was still enabled in the diagnostics sub-process :roll_eyes: 

Patch:
diff --git a/lib/Application.php b/lib/Application.php
index 93bdac23..387ab8ee 100644
--- a/lib/Application.php
+++ b/lib/Application.php
@@ -72,6 +72,7 @@ class Application extends SymfonyApplication
     {
         $definition = parent::getDefaultInputDefinition();
         $definition->addOption(new InputOption('working-dir', 'd', InputOption::VALUE_REQUIRED, 'Working directory'));
+        $definition->addOption(new InputOption('config-extra', null, InputOption::VALUE_REQUIRED, 'Additional config to apply (JSON string)'));
 
         return $definition;
     }
diff --git a/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php b/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php
index 3074f45f..dd644fc8 100644
--- a/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php
+++ b/lib/Extension/LanguageServer/Command/DiagnosticsCommand.php
@@ -15,7 +15,7 @@ use function Amp\Promise\wait;
 class DiagnosticsCommand extends Command
 {
     public const NAME = 'language-server:diagnostics';
-    const PARAM_URI = 'uri';
+    private const PARAM_URI = 'uri';
 
 
     public function __construct(private DiagnosticsProvider $provider)
diff --git a/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php b/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php
index e72e5211..c3dc4950 100644
--- a/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php
+++ b/lib/Extension/LanguageServer/DiagnosticProvider/OutsourcedDiagnosticsProvider.php
@@ -7,6 +7,7 @@ use Amp\Process\Process;
 use Amp\Process\ProcessException;
 use Amp\Promise;
 use Phpactor\Amp\Process\ProcessUtil;
+use Phpactor\Extension\WorseReflection\WorseReflectionExtension;
 use Phpactor\LanguageServerProtocol\Diagnostic;
 use Phpactor\LanguageServerProtocol\TextDocumentItem;
 use Phpactor\LanguageServer\Core\Diagnostics\DiagnosticsProvider;
@@ -32,7 +33,8 @@ class OutsourcedDiagnosticsProvider implements DiagnosticsProvider
     {
         return call(function () use ($textDocument) {
             $process = new Process(array_merge($this->command, [
-                '--uri=' . $textDocument->uri
+                '--uri=' . $textDocument->uri,
+                sprintf('--config-extra=%s', sprintf('{"%s": false}', WorseReflectionExtension::PARAM_ENABLE_CONTEXT_LOCATION))
             ]), $this->cwd);
             $pid = yield $process->start();
 
diff --git a/lib/Phpactor.php b/lib/Phpactor.php
index 49ff3899..e5871790 100644
--- a/lib/Phpactor.php
+++ b/lib/Phpactor.php
@@ -117,6 +117,24 @@ class Phpactor
             $config[FilePathResolverExtension::PARAM_PROJECT_ROOT] = $projectRoot;
         }
 
+        if ($input->hasParameterOption('--config-extra')) {
+            $rawJson = $input->getParameterOption('--config-extra');
+            if (!is_string($rawJson)) {
+                throw new RuntimeException(sprintf(
+                    'Expected string for config-extra, got: %s',
+                    gettype($rawJson)
+                ));
+            }
+            $extraConfig = json_decode($rawJson, true);
+            if (!is_array($extraConfig)) {
+                throw new RuntimeException(sprintf(
+                    'Invalid JSON passed as config-extra: %s',
+                    $rawJson
+                ));
+            }
+            $config = array_merge($config, $extraConfig);
+        }
+
         if (!isset($config[CoreExtension::PARAM_XDEBUG_DISABLE]) || $config[CoreExtension::PARAM_XDEBUG_DISABLE]) {
             $xdebug = new XdebugHandler('PHPACTOR');
             $xdebug->check();
diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php
index ea1530bb..e3056411 100644
--- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php
+++ b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/MissingMemberProvider.php
@@ -217,7 +217,7 @@ class MissingMemberProvider implements DiagnosticProvider
                 }
             );
             yield new DiagnosticExample(
-                title: 'enum static method call',
+                title: 'enum static method not existing',
                 source: <<<'PHP'
                     <?php
 
@@ -225,9 +225,25 @@ class MissingMemberProvider implements DiagnosticProvider
                     {
                     }
 
-                    Foobar::cases();
+                    Foobar::foobar();
                     PHP,
                 valid: false,
+                assertion: function (Diagnostics $diagnostics): void {
+                    Assert::assertCount(1, $diagnostics);
+                }
+            );
+            yield new DiagnosticExample(
+                title: 'enum cases',
+                source: <<<'PHP'
+                    <?php
+
+                    enum Foobar
+                    {
+                    }
+
+                    Foobar::cases();
+                    PHP,
+                valid: true,
                 assertion: function (Diagnostics $diagnostics): void {
                     Assert::assertCount(0, $diagnostics);
                 }
diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php
index 2e7e467f..7a596c70 100644
--- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php
+++ b/lib/WorseReflection/Bridge/TolerantParser/Reflection/ReflectionEnum.php
@@ -56,7 +56,8 @@ class ReflectionEnum extends AbstractReflectionClass implements CoreReflectionEn
         }
         try {
             $enumType = $this->isBacked() ? 'BackedEnum' : 'UnitEnum';
-            $enumMethods = $this->serviceLocator()->reflector()->reflectInterface($enumType)->members();
+            $interface = $this->serviceLocator()->reflector()->reflectInterface($enumType);
+            $enumMethods = $interface->members();
             /** @phpstan-ignore-next-line It is fine */
             return $members->merge($enumMethods)->map(
                 fn (ReflectionMember $member) => $member->withClass($this)


Test patch:
diff --git a/lib/WorseReflection/Tests/Inference/enum/enum_case.test b/lib/WorseReflection/Tests/Inference/enum/enum_case.test
index 169c2004..650dc8cd 100644
--- a/lib/WorseReflection/Tests/Inference/enum/enum_case.test
+++ b/lib/WorseReflection/Tests/Inference/enum/enum_case.test
@@ -9,6 +9,7 @@ wrAssertType('enum(Foo::FOO)', Foo::FOO);
 wrAssertType('<missing>', Foo::FOO->value);
 wrAssertType('string', Foo::FOO->name);
 wrAssertType('"bar"', Foo::BAR);
+wrAssertType('UnitEnumCase[]', Foo::cases());
 
 class UnitEnumCase {
     public string $name;
diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php
index 3a41d8b1..4a0b7dde 100644
--- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php
+++ b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionEnumTest.php
@@ -3,6 +3,7 @@
 namespace Phpactor\WorseReflection\Tests\Integration\Bridge\TolerantParser\Reflection;
 
 use Phpactor\WorseReflection\Core\Reflection\ReflectionEnumCase;
+use Phpactor\WorseReflection\Core\Reflection\ReflectionMethod;
 use Phpactor\WorseReflection\Core\Type\EnumBackedCaseType;
 use Phpactor\WorseReflection\Core\Type\EnumCaseType;
 use Phpactor\WorseReflection\Core\Type\MissingType;
@@ -79,6 +80,7 @@ class ReflectionEnumTest extends IntegrationTestCase
         function (ReflectionEnum $class): void {
             $this->assertCount(4, $class->members());
             $this->assertInstanceOf(ReflectionEnumCase::class, $class->members()->get('FOOBAR'));
+            $this->assertInstanceOf(ReflectionMethod::class, $class->members()->get('cases'));
         },
         ];