Repository: phpactor/phpactor (php)

Issue:
class-string is not able to find references/methods
Given this code:

```php
<?php

namespace Demo;

class Foo
{
    public static function demo(): string
    {
    }
}

/** @var class-string<\Demo\Foo> $bar **/
$bar = Foo::class; // Correctly hinted to class-string<\Demo\Foo>

$x = $bar::demo(); // Diagnostics: Method 'Demo' does not exist on class "Demo\$bar"
```

I believe $bar::demo() should be correctly resolved?
There is no hover information either.

Pull request:
Gh 2238: fix class-stinrg<Foo> being used as scoped resolution qualifier
fixes #2238 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb28ea6f..4c122e18 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ Features:
 
 Bug fixes:
 
+  - Fix use of class-string<Foo> variable as static scope resolution qualifier #2238
   - URL decode root URI - fixes issues with special chars in path #2228
   - Do not deduplicate suggestions of different types (e.g. prop/method with same name) #2214
   - Fix list assignment #2226
diff --git a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php
index ebf74e69..1b7078c4 100644
--- a/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php
+++ b/lib/WorseReflection/Bridge/TolerantParser/Diagnostics/UndefinedVariableProvider.php
@@ -241,7 +241,7 @@ class UndefinedVariableProvider implements DiagnosticProvider
                 Assert::assertCount(0, $diagnostics);
             }
         );
-                    
+
         yield new DiagnosticExample(
             title: 'static',
             source: <<<'PHP'
diff --git a/lib/WorseReflection/Core/DefaultResolverFactory.php b/lib/WorseReflection/Core/DefaultResolverFactory.php
index e7dac9a5..a8f38be7 100644
--- a/lib/WorseReflection/Core/DefaultResolverFactory.php
+++ b/lib/WorseReflection/Core/DefaultResolverFactory.php
@@ -125,7 +125,7 @@ final class DefaultResolverFactory
             UseVariableName::class => new UseVariableNameResolver(),
             Variable::class => new VariableResolver(),
             MemberAccessExpression::class => new MemberAccessExpressionResolver($this->nodeContextFromMemberAccess),
-            ScopedPropertyAccessExpression::class => new ScopedPropertyAccessResolver($this->nodeTypeConverter, $this->nodeContextFromMemberAccess),
+            ScopedPropertyAccessExpression::class => new ScopedPropertyAccessResolver($this->nodeContextFromMemberAccess),
             CallExpression::class => new CallExpressionResolver(),
             ParenthesizedExpression::class => new ParenthesizedExpressionResolver(),
             BinaryExpression::class => new BinaryExpressionResolver(),
diff --git a/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php b/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php
index 59789749..8c6a0f97 100644
--- a/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php
+++ b/lib/WorseReflection/Core/Inference/Resolver/ScopedPropertyAccessResolver.php
@@ -6,17 +6,17 @@ use Microsoft\PhpParser\Node;
 use Microsoft\PhpParser\Node\Expression\ScopedPropertyAccessExpression;
 use Microsoft\PhpParser\Node\Expression\Variable;
 use Phpactor\WorseReflection\Core\Inference\Frame;
-use Phpactor\WorseReflection\Core\Inference\NodeToTypeConverter;
 use Phpactor\WorseReflection\Core\Inference\NodeContext;
 use Phpactor\WorseReflection\Core\Inference\Resolver;
 use Phpactor\WorseReflection\Core\Inference\NodeContextResolver;
 use Phpactor\WorseReflection\Core\Inference\Resolver\MemberAccess\NodeContextFromMemberAccess;
+use Phpactor\WorseReflection\Core\TypeFactory;
+use Phpactor\WorseReflection\Core\Type\ClassStringType;
 use Phpactor\WorseReflection\Core\Type\ClassType;
 
 class ScopedPropertyAccessResolver implements Resolver
 {
     public function __construct(
-        private NodeToTypeConverter $nodeTypeConverter,
         private NodeContextFromMemberAccess $nodeContextFromMemberAccess
     ) {
     }
@@ -41,7 +41,11 @@ class ScopedPropertyAccessResolver implements Resolver
             $name = $node->scopeResolutionQualifier->getText();
         }
 
-        $classType = $this->nodeTypeConverter->resolve($node, (string)$name);
+        $classType = $resolver->resolveNode($frame, $node->scopeResolutionQualifier)->type();
+
+        if ($classType instanceof ClassStringType && $classType->className()) {
+            $classType = TypeFactory::reflectedClass($resolver->reflector(), $classType->className());
+        }
 
         return $this->nodeContextFromMemberAccess->infoFromMemberAccess($resolver, $frame, $classType, $node);
     }


Test patch:
diff --git a/lib/WorseReflection/Tests/Inference/type/class-string-static-call.test b/lib/WorseReflection/Tests/Inference/type/class-string-static-call.test
new file mode 100644
index 00000000..f391b5d8
--- /dev/null
+++ b/lib/WorseReflection/Tests/Inference/type/class-string-static-call.test
@@ -0,0 +1,15 @@
+<?php
+
+namespace Demo;
+
+class Foo
+{
+    public static function demo(): string
+    {
+    }
+}
+
+$bar = Foo::class;
+
+wrAssertType('class-string<Demo\Foo>', $bar);
+wrAssertType('string', $bar::demo());