Repository: phpactor/phpactor (php)

Issue:
phpactor does not correctly understand signatures that return self implemented by trait.
```php
<?php

trait SingletonTrait {
	public static function getInstance() : self {}
}

class A {
	use SingletonTrait;

	public function foo() : void {}
}

A::getInstance()->foo(); // Method "foo" does not exist on class "SingletonTrait"
```

Pull request description:
Failing test for gh-2116
Would fix #2116 

The wrong type is being returned here:

https://github.com/phpactor/phpactor/blob/814a043aa5dbd3d953bc8201fa84e982187d4df2/lib/WorseReflection/Core/Util/NodeUtil.php#L224-L227

and need to think about how to properly contextualize this (hint: not here)

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b202f57b..9aba0bdb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ Changelog
 
 Bug fixes:
 
+  - Fix resolution of `self` type (esp. in relation to traits) #2116
   - Fix different virtual member types with the same name replacing eachother #2108
   - Specify maximimum size (255 chars) for string literal types #2144
   - Fix docblock parser with `$this` when used as generic argument #2092
diff --git a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php b/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php
index edbac983..cb76e99d 100644
--- a/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php
+++ b/lib/WorseReflection/Bridge/TolerantParser/Reflection/TypeResolver/DeclaredMemberTypeResolver.php
@@ -8,6 +8,7 @@ use Phpactor\WorseReflection\Core\Type;
 use Phpactor\WorseReflection\Core\ClassName;
 use Microsoft\PhpParser\Node;
 use Phpactor\WorseReflection\Core\TypeFactory;
+use Phpactor\WorseReflection\Core\Type\SelfType;
 use Phpactor\WorseReflection\Core\Util\NodeUtil;
 use Phpactor\WorseReflection\Reflector;
 
@@ -57,6 +58,16 @@ class DeclaredMemberTypeResolver
             return TypeFactory::undefined();
         }
 
-        return NodeUtil::typeFromQualfiedNameLike($this->reflector, $tolerantNode, $tolerantType, $className);
+        $type =  NodeUtil::typeFromQualfiedNameLike($this->reflector, $tolerantNode, $tolerantType, $className);
+
+        $type = $type->map(function (Type $type) use ($className) {
+            if ($className && $type instanceof SelfType) {
+                return new SelfType(TypeFactory::reflectedClass($this->reflector, $className));
+
+            }
+            return $type;
+        });
+
+        return $type;
     }
 }
diff --git a/lib/WorseReflection/Core/Util/NodeUtil.php b/lib/WorseReflection/Core/Util/NodeUtil.php
index 6106df19..1756b4e2 100644
--- a/lib/WorseReflection/Core/Util/NodeUtil.php
+++ b/lib/WorseReflection/Core/Util/NodeUtil.php
@@ -23,6 +23,7 @@ use Phpactor\WorseReflection\Core\Type;
 use Phpactor\WorseReflection\Core\TypeFactory;
 use Phpactor\WorseReflection\Core\Type\IntersectionType;
 use Phpactor\WorseReflection\Core\Type\MissingType;
+use Phpactor\WorseReflection\Core\Type\SelfType;
 use Phpactor\WorseReflection\Core\Type\UnionType;
 use Phpactor\WorseReflection\Reflector;
 
@@ -222,8 +223,7 @@ class NodeUtil
             }
 
             if ($text === 'self') {
-                $class = self::nodeContainerClassLikeDeclaration($node);
-                return TypeFactory::reflectedClass($reflector, $class->getNamespacedName()->__toString());
+                return new SelfType();
             }
 
             if ($text === 'static') {


Test patch:
diff --git a/lib/WorseReflection/Tests/Inference/type/self_context_trait.test b/lib/WorseReflection/Tests/Inference/type/self_context_trait.test
new file mode 100644
index 00000000..cbc2cc3e
--- /dev/null
+++ b/lib/WorseReflection/Tests/Inference/type/self_context_trait.test
@@ -0,0 +1,13 @@
+<?php
+
+trait SingletonTrait {
+	public static function getInstance() : self {}
+}
+
+class A {
+	use SingletonTrait;
+
+	public function foo() : void {}
+}
+
+wrAssertType('A', A::getInstance());
diff --git a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php
index 74cfc75e..428ea602 100644
--- a/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php
+++ b/lib/WorseReflection/Tests/Integration/Bridge/TolerantParser/Reflection/ReflectionMethodTest.php
@@ -141,7 +141,7 @@ class ReflectionMethodTest extends IntegrationTestCase
                 $this->assertEquals(TypeFactory::array(), $methods->get('method4')->returnType());
                 $this->assertEquals(ClassName::fromString('Test\Barfoo'), $methods->get('method5')->returnType()->name);
                 $this->assertEquals(ClassName::fromString('Acme\Post'), $methods->get('method6')->returnType()->name);
-                $this->assertEquals(ClassName::fromString('Test\Foobar'), $methods->get('method7')->returnType()->name);
+                $this->assertEquals('self(Test\Foobar)', $methods->get('method7')->returnType()->__toString());
                 $this->assertEquals(TypeFactory::iterable(), $methods->get('method8')->returnType());
                 $this->assertEquals(TypeFactory::callable(), $methods->get('method9')->returnType());
                 $this->assertEquals(TypeFactory::resource(), $methods->get('method10')->returnType());