Repository: Roave/BetterReflection (php)

Issue:
`ReflectionFunctionAbstract` has incorrect `locatedSource` value
When trying to get the location of the method which is inherited from the parent class fails with the `InvalidNodePosition` exception, but from a deeper look, it seems that the `$this->locatedSource->getSource()` method returns not the parent class source but the source of which implemented it.

From looking around the code seems, like it should return `$this->getDeclaringClass()->getLocatedSource()->getSource()` for `ReflectionMethod` and `$this->locatedSource->getSource()`  for `ReflectionFunction`

Reproducible code:
<details>
  <summary>Click to expand!</summary>

```php
<?php

declare(strict_types=1);

use Roave\BetterReflection\Reflection\ReflectionClass;
use Roave\BetterReflection\Reflection\ReflectionEnum;

require_once __DIR__ . '/vendor/autoload.php';

$classRef = ReflectionClass::createFromName(ReflectionEnum::class);
$methodRef = $classRef->getMethod('__toString');
$methodRef->getStartColumn();
```

</details>

Pull request:
Fixed located source for parent methods and methods from traits
Fixes #964

Patch:
diff --git a/src/Reflection/ReflectionClass.php b/src/Reflection/ReflectionClass.php
index 159530db..181339b9 100644
--- a/src/Reflection/ReflectionClass.php
+++ b/src/Reflection/ReflectionClass.php
@@ -251,7 +251,7 @@ class ReflectionClass implements Reflection
         $createMethod = fn (?string $aliasMethodName): ReflectionMethod => ReflectionMethod::createFromNode(
             $this->reflector,
             $methodAst,
-            $this->locatedSource,
+            $method->getLocatedSource(),
             $method->getDeclaringClass()->getDeclaringNamespaceAst(),
             $method->getDeclaringClass(),
             $this,
@@ -288,7 +288,7 @@ class ReflectionClass implements Reflection
                         fn (ReflectionMethod $method): ReflectionMethod => ReflectionMethod::createFromNode(
                             $this->reflector,
                             $method->getAst(),
-                            $this->locatedSource,
+                            $method->getLocatedSource(),
                             $method->getDeclaringClass()->getDeclaringNamespaceAst(),
                             $method->getDeclaringClass(),
                             $method->getImplementingClass(),


Test patch:
diff --git a/test/unit/Reflection/ReflectionMethodTest.php b/test/unit/Reflection/ReflectionMethodTest.php
index c5f3783c..c1034c45 100644
--- a/test/unit/Reflection/ReflectionMethodTest.php
+++ b/test/unit/Reflection/ReflectionMethodTest.php
@@ -25,6 +25,7 @@ use Roave\BetterReflection\Reflector\DefaultReflector;
 use Roave\BetterReflection\Reflector\Reflector;
 use Roave\BetterReflection\SourceLocator\Ast\Locator;
 use Roave\BetterReflection\SourceLocator\SourceStubber\SourceStubber;
+use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
 use Roave\BetterReflection\SourceLocator\Type\ComposerSourceLocator;
 use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator;
 use Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator;
@@ -680,4 +681,75 @@ PHP;
 
         self::assertCount(2, $attributes);
     }
+
+    public function testLocatedSourceForParentMethod(): void
+    {
+        $parentPhp = <<<'PHP'
+            <?php
+
+            class Foo
+            {
+                public function method(): void
+                {
+                }
+            }
+        PHP;
+
+        $childPhp = <<<'PHP'
+            <?php
+
+            class Bar extends Foo
+            {
+            }
+        PHP;
+
+        $reflector = new DefaultReflector(new AggregateSourceLocator([
+            new StringSourceLocator($parentPhp, $this->astLocator),
+            new StringSourceLocator($childPhp, $this->astLocator),
+        ]));
+
+        $classReflection  = $reflector->reflectClass('Bar');
+        $methodReflection = $classReflection->getMethod('method');
+
+        self::assertStringMatchesFormat(
+            '%Aclass Foo%A{%A}%A',
+            $methodReflection->getLocatedSource()->getSource(),
+        );
+    }
+
+    public function testLocatedSourceForTraitMethod(): void
+    {
+        $parentPhp = <<<'PHP'
+            <?php
+
+            trait Foo
+            {
+                public function method(): void
+                {
+                }
+            }
+        PHP;
+
+        $childPhp = <<<'PHP'
+            <?php
+
+            class Bar
+            {
+                use Foo;
+            }
+        PHP;
+
+        $reflector = new DefaultReflector(new AggregateSourceLocator([
+            new StringSourceLocator($parentPhp, $this->astLocator),
+            new StringSourceLocator($childPhp, $this->astLocator),
+        ]));
+
+        $classReflection  = $reflector->reflectClass('Bar');
+        $methodReflection = $classReflection->getMethod('method');
+
+        self::assertStringMatchesFormat(
+            '%Atrait Foo%A{%A}%A',
+            $methodReflection->getLocatedSource()->getSource(),
+        );
+    }
 }