Repository: phpactor/phpactor (php)

Issue:
Phpactor does not resolve Pest's Expectation mixin
On a clean project with only pest installed, phpactor cannot find mixin functions from the Pest\Expectation class:

![2023-12-10-15:57:01-screenshot](https://github.com/phpactor/phpactor/assets/5471818/f04a7f43-b710-47bb-8567-7a23ca3d5145)

The expect function resolves into a Pest\Expectation object, declared like so:

```php
/**
 * @template TValue
 *
 * @property OppositeExpectation $not Creates the opposite expectation.
 * @property EachExpectation $each Creates an expectation on each element on the traversable value.
 * @property PendingArchExpectation $classes
 * @property PendingArchExpectation $traits
 * @property PendingArchExpectation $interfaces
 * @property PendingArchExpectation $enums
 *
 * @mixin Mixins\Expectation<TValue>
 * @mixin PendingArchExpectation
 */
final class Expectation
{
...
```

The `Mixins\Expectation<TValue>`  contains a bunch of methods that aren't recognized, like `toBe` and `toBeTrue`. Is this a configuration issue on my side?

Pull request description:
Gh 2463 mixin gen
Fixes #2463

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d81a873..3dadac4c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,10 +7,11 @@ Features:
 
   - Completion suggestions filtered by accepting type #2456
 
-Imporvements:
+Improvements:
 
   - Clear document diagnostic cache on save #2458
   - Skip parent parameters on complete constructor #2471 @mamazu
+  - Support generics on `@mixin` #2463
 
 Bug fixes:
 
diff --git a/lib/DocblockParser/Ast/Tag/MixinTag.php b/lib/DocblockParser/Ast/Tag/MixinTag.php
index fdd3ddb3..42a1fed5 100644
--- a/lib/DocblockParser/Ast/Tag/MixinTag.php
+++ b/lib/DocblockParser/Ast/Tag/MixinTag.php
@@ -3,7 +3,7 @@
 namespace Phpactor\DocblockParser\Ast\Tag;
 
 use Phpactor\DocblockParser\Ast\TagNode;
-use Phpactor\DocblockParser\Ast\Type\ClassNode;
+use Phpactor\DocblockParser\Ast\TypeNode;
 use Phpactor\DocblockParser\Ast\Token;
 
 class MixinTag extends TagNode
@@ -13,11 +13,11 @@ class MixinTag extends TagNode
         'class'
     ];
 
-    public function __construct(public Token $tag, public ?ClassNode $class)
+    public function __construct(public Token $tag, public ?TypeNode $class)
     {
     }
 
-    public function class(): ?ClassNode
+    public function class(): ?TypeNode
     {
         return $this->class;
     }
diff --git a/lib/DocblockParser/Parser.php b/lib/DocblockParser/Parser.php
index 55990620..dcdae033 100644
--- a/lib/DocblockParser/Parser.php
+++ b/lib/DocblockParser/Parser.php
@@ -507,7 +507,7 @@ final class Parser
 
         if ($this->tokens->if(Token::T_LABEL)) {
             $type = $this->parseTypes();
-            if (!$type instanceof ClassNode) {
+            if (!$type instanceof ClassNode && !$type instanceof GenericNode) {
                 $type = null;
             }
         }


Test patch:
diff --git a/lib/DocblockParser/Tests/Unit/Printer/examples/mixin-generic.test b/lib/DocblockParser/Tests/Unit/Printer/examples/mixin-generic.test
new file mode 100644
index 00000000..2a0b7c2a
--- /dev/null
+++ b/lib/DocblockParser/Tests/Unit/Printer/examples/mixin-generic.test
@@ -0,0 +1,13 @@
+/**
+ * @mixin Foobar<Baz>
+ */
+---
+Docblock: = 
+ ElementList: = /**
+ * 
+  MixinTag: = @mixin
+   GenericNode: = 
+    ClassNode: = Foobar<
+    TypeList: = 
+     ClassNode: = Baz>
+ */
diff --git a/lib/WorseReflection/Tests/Inference/reflection/mixin_generic.test b/lib/WorseReflection/Tests/Inference/reflection/mixin_generic.test
new file mode 100644
index 00000000..89f760fa
--- /dev/null
+++ b/lib/WorseReflection/Tests/Inference/reflection/mixin_generic.test
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @template TValue
+ */
+class A
+{
+    public function toBeTrue(): bool
+    {
+    }
+}
+
+/**
+ * @mixin A<string>
+ */
+class B
+{
+}
+
+$b = new B();
+wrAssertType('bool', $b->toBeTrue());