CodeLocationMissing is thrown, also unable to compile initializer
DanielBadura opened this issue ยท 11 comments
Hello there ๐
Since some time now the CI job for the BC-Check is failing on our Repo. This happened after merging this PR here: patchlevel/event-sourcing#297
After merging we got this action output: https://github.com/patchlevel/event-sourcing/actions/runs/3278010376/jobs/5395925767
I check it out locally and run it with verbose settings and some dump here and there.
....
string(75) "Patchlevel\EventSourcing\Projection\ProjectorStore\ProjectorStateCollection"
string(71) "Patchlevel\EventSourcing\Projection\ProjectorStore\DuplicateProjectorId"
string(57) "Patchlevel\EventSourcing\Projection\SyncProjectorListener"
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\SyncProjectorListener::__construct() in file /src/Projection/SyncProjectorListener.php (line 14)
string(55) "Patchlevel\EventSourcing\Projection\ProjectorRepository"
string(61) "Patchlevel\EventSourcing\Projection\MetadataProjectorResolver"
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\MetadataProjectorResolver::__construct() in file /src/Projection/MetadataProjectorResolver.php (line 17)
string(56) "Patchlevel\EventSourcing\Projection\DefaultProjectionist"
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\DefaultProjectionist::__construct() in file /src/Projection/DefaultProjectionist.php (line 23)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\DefaultProjectionist::boot() in file /src/Projection/DefaultProjectionist.php (line 28)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\DefaultProjectionist::run() in file /src/Projection/DefaultProjectionist.php (line 75)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\DefaultProjectionist::teardown() in file /src/Projection/DefaultProjectionist.php (line 117)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\DefaultProjectionist::remove() in file /src/Projection/DefaultProjectionist.php (line 148)
string(49) "Patchlevel\EventSourcing\Projection\Projectionist"
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\Projectionist::boot() in file /src/Projection/Projectionist.php (line 11)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\Projectionist::run() in file /src/Projection/Projectionist.php (line 16)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\Projectionist::teardown() in file /src/Projection/Projectionist.php (line 18)
[BC] SKIPPED: Unable to compile initializer in method Patchlevel\EventSourcing\Projection\Projectionist::remove() in file /src/Projection/Projectionist.php (line 20)
string(51) "Patchlevel\EventSourcing\Projection\ProjectorStatus"
string(4) "name"
string(51) "Patchlevel\EventSourcing\Projection\ProjectorStatus"
string(51) "Patchlevel\EventSourcing\Projection\ProjectorStatus"
string(6) "string"
NULL
NULL
In CodeLocationMissing.php line 13:
[Roave\BetterReflection\Reflection\Exception\CodeLocationMissing]
Code location is missing
Exception trace:
at /app/tools/vendor/roave/better-reflection/src/Reflection/Exception/CodeLocationMissing.php:13
Roave\BetterReflection\Reflection\Exception\CodeLocationMissing::create() at /app/tools/vendor/roave/better-reflection/src/Reflection/ReflectionProperty.php:367
Roave\BetterReflection\Reflection\ReflectionProperty->getStartLine() at /app/tools/vendor/roave/backward-compatibility-check/src/DetectChanges/BCBreak/PropertyBased/MultipleChecksOnAProperty.php:31
Roave\BackwardCompatibility\DetectChanges\BCBreak\PropertyBased\MultipleChecksOnAProperty->multipleChecks() at /app/tools/vendor/roave/backward-compatibility-check/src/Changes.php:81
Roave\BackwardCompatibility\Changes->getIterator() at /app/tools/vendor/roave/backward-compatibility-check/src/DetectChanges/BCBreak/ClassBased/PropertyChanged.php:38
Roave\BackwardCompatibility\DetectChanges\BCBreak\ClassBased\PropertyChanged->checkSymbols() at /app/tools/vendor/roave/backward-compatibility-check/src/Changes.php:81
Roave\BackwardCompatibility\Changes->getIterator() at /app/tools/vendor/roave/backward-compatibility-check/src/DetectChanges/BCBreak/ClassBased/MultipleChecksOnAClass.php:35
Roave\BackwardCompatibility\DetectChanges\BCBreak\ClassBased\MultipleChecksOnAClass->multipleChecks() at /app/tools/vendor/roave/backward-compatibility-check/src/Changes.php:81
Roave\BackwardCompatibility\Changes->getIterator() at /app/tools/vendor/roave/backward-compatibility-check/src/DetectChanges/BCBreak/ClassBased/MultipleChecksOnAClass.php:35
Roave\BackwardCompatibility\DetectChanges\BCBreak\ClassBased\MultipleChecksOnAClass->multipleChecks() at /app/tools/vendor/roave/backward-compatibility-check/src/Changes.php:81
Roave\BackwardCompatibility\Changes->getIterator() at /app/tools/vendor/roave/backward-compatibility-check/src/CompareClasses.php:94
Roave\BackwardCompatibility\CompareClasses->examineSymbol() at /app/tools/vendor/roave/backward-compatibility-check/src/CompareClasses.php:63
Roave\BackwardCompatibility\CompareClasses->makeSymbolsIterator() at /app/tools/vendor/roave/backward-compatibility-check/src/Changes.php:81
Roave\BackwardCompatibility\Changes->getIterator() at /app/tools/vendor/roave/backward-compatibility-check/src/Formatter/SymfonyConsoleTextFormatter.php:18
Roave\BackwardCompatibility\Formatter\SymfonyConsoleTextFormatter->write() at /app/tools/vendor/roave/backward-compatibility-check/src/Command/AssertBackwardsCompatible.php:168
Roave\BackwardCompatibility\Command\AssertBackwardsCompatible->execute() at /app/tools/vendor/symfony/console/Command/Command.php:308
Symfony\Component\Console\Command\Command->run() at /app/tools/vendor/symfony/console/Application.php:1014
Symfony\Component\Console\Application->doRunCommand() at /app/tools/vendor/symfony/console/Application.php:301
Symfony\Component\Console\Application->doRun() at /app/tools/vendor/symfony/console/Application.php:171
Symfony\Component\Console\Application->run() at /app/tools/vendor/roave/backward-compatibility-check/bin/roave-backward-compatibility-check.php:354
Roave\ApiCompareCli\{closure}() at /app/tools/vendor/roave/backward-compatibility-check/bin/roave-backward-compatibility-check.php:355
require_once() at /app/tools/vendor/roave/backward-compatibility-check/bin/roave-backward-compatibility-check:6
include() at /app/tools/vendor/bin/roave-backward-compatibility-check:120
So i tried to figure out what is happening but couldnt reproduce this in BetterReflection. I tried to write some tests to get a minimal reproducing fail but without success. I think this has something to do with initliazing a default value in the method and our new enum.
public function testClassWithNewInitializerOfEnum(): void
{
$php = <<<'PHP'
<?php
enum Foo: string
{
case New = 'new';
}
class Bar {
public function __construct(private Foo $foo = Foo::New) {}
}
PHP;
$reflector = new DefaultReflector(new StringSourceLocator($php, $this->astLocator));
$classReflection = $reflector->reflectClass('Bar');
$method = $classReflection->getMethod('__construct');
$property = $method->getParameter('foo');
$property->getStartLine();
}
public function testClassWithNestedNewInitializer(): void
{
$php = <<<'PHP'
<?php
final class Foo {
public function __construct() {}
}
class Bar {
public function __construct(private Foo $foo = new Foo()) {}
}
class Baz {
public function __construct(private Bar $bar = new Bar()) {}
}
PHP;
$reflector = new DefaultReflector(new StringSourceLocator($php, $this->astLocator));
$classReflection = $reflector->reflectClass('Baz');
$method = $classReflection->getMethod('__construct');
$property = $method->getParameter('bar');
$property->getStartLine();
}
public function testInterfaceWithNewInitializer(): void
{
$php = <<<'PHP'
<?php
class Foo {
public function __construct(private ?array $bro = null) {}
}
interface Bar {
public function __invoke(Foo $foo = new Foo()): void;
}
class Baz implements Bar {
public function __invoke(Foo $foo = new Foo()): void {}
}
PHP;
$reflector = new DefaultReflector(new StringSourceLocator($php, $this->astLocator));
$classReflection = $reflector->reflectClass('Baz');
$method = $classReflection->getMethod('__invoke');
$property = $method->getParameter('foo');
$property->getStartLine();
$property->getType()->getClass()->getMethod('__construct')->getParameter('bro')->getStartLine();
}
Maybe you could give me a hint in the right direction how to get the culprit out of this? Is this maybe a bug in the underlying php-parser?
@DanielBadura in the above tests, do you experience any kind of failure?
It seems to fail here: Roave\BetterReflection\Reflection\ReflectionProperty->getStartLine()
Perhaps it cannot find the value
reflection property for an ENUM? value
is declared in php-src
, so it may be that ๐ค
I think I replicated this issue in phpstan/phpstan-src#2026
Yep, seems like it's really about the core-provided reflection symbol.
In the adapters, we can try-catch this stuff.
In this component (where we don't use the adapters) we either catch {}
, or we have to provide a code location for ENUMs (unlikely)
@DanielBadura in the above tests, do you experience any kind of failure?
It seems to fail here:
Roave\BetterReflection\Reflection\ReflectionProperty->getStartLine()
Perhaps it cannot find the
value
reflection property for an ENUM?value
is declared inphp-src
, so it may be that ๐ค
No, the testcases all did not throw, thats why i could not find the real issue.
Tried now this here based on the linked issue:
public function testEnumStartLine(): void
{
$php = <<<'PHP'
<?php
enum Foo: string
{
case New = 'new';
}
PHP;
$reflector = new DefaultReflector(new StringSourceLocator($php, $this->astLocator));
$classReflection = $reflector->reflectClass('Foo');
foreach ($classReflection->getMethods() as $method) {
$method->getStartLine();
}
}
Which results into an error which seems a different one than the CodeLocationMissing, no?:
1) Roave\BetterReflectionTest\Reflection\ReflectionClassTest::testEnumStartLine
Roave\BetterReflection\Reflector\Exception\IdentifierNotFound: Roave\BetterReflection\Reflection\ReflectionClass "UnitEnum" could not be found in the located source
/app/src/Reflector/Exception/IdentifierNotFound.php:26
/app/src/Reflector/DefaultReflector.php:35
/app/src/Reflection/ReflectionClass.php:1257
/app/src/Reflection/ReflectionClass.php:1465
/app/src/Reflection/ReflectionClass.php:938
/app/src/Reflection/ReflectionClass.php:906
/app/test/unit/Reflection/ReflectionClassTest.php:2896
@DanielBadura in your example, your DefaultReflector
only knows about the StringSourceLocator
: you will need to provide a source locator for internal PHP symbols too :)
Yes, thanks. This was missing, now i get with this
public function testEnumStartLine(): void
{
$php = <<<'PHP'
<?php
enum Foo: string
{
case New = 'new';
}
PHP;
$reflector = new DefaultReflector(
new AggregateSourceLocator([
new StringSourceLocator($php, $this->astLocator),
new PhpInternalSourceLocator($this->astLocator, BetterReflectionSingleton::instance()->sourceStubber()),
])
);
$classReflection = $reflector->reflectClass('Foo');
foreach ($classReflection->getProperties() as $property) {
$property->getStartLine();
}
}
This error:
There was 1 error:
1) Roave\BetterReflectionTest\Reflection\ReflectionClassTest::testEnumStartLine
Roave\BetterReflection\Reflection\Exception\CodeLocationMissing: Code location is missing
/app/src/Reflection/Exception/CodeLocationMissing.php:13
/app/src/Reflection/ReflectionProperty.php:367
/app/test/unit/Reflection/ReflectionClassTest.php:2903
Yeah, that's indeed the reproducer.
The exception is correct/expected here.
What needs to happen in BetterReflection (in the adapter) is a try-catch.
In BackwardCompatibilityCheck, this needs to be hardened: https://github.com/Roave/BackwardCompatibilityCheck/blob/5e31508bae4c8d3f90f7dfbc2a3cbca735fa254e/src/DetectChanges/BCBreak/FunctionBased/MultipleChecksOnAFunction.php#L42-L43
I would say that SymbolStartLocation
should be expanded to handle that.
I'll prepare PR for adapters so it should solve PHPStan issue.
In BackwardCompatibilityCheck, this needs to be hardened: https://github.com/Roave/BackwardCompatibilityCheck/blob/5e31508bae4c8d3f90f7dfbc2a3cbca735fa254e/src/DetectChanges/BCBreak/FunctionBased/MultipleChecksOnAFunction.php#L42-L43
I would say that
SymbolStartLocation
should be expanded to handle that.
I think you linked the wrong place since the exception in this case right now i thrown from the PropertyBased
one. https://github.com/Roave/BackwardCompatibilityCheck/blob/5e31508bae4c8d3f90f7dfbc2a3cbca735fa254e/src/DetectChanges/BCBreak/PropertyBased/MultipleChecksOnAProperty.php#L30
So i could try to harden it there, but i think i should wait until the PR mentioned by @kukulich is finished, right?
@DanielBadura I will fix Roave\BetterReflection\Reflection\Adapter\ReflectionProperty
(adapter that mimics native reflection) but "Backward Compatibility Check" uses Roave\BetterReflection\Reflection\ReflectionProperty
@DanielBadura Roave/BackwardCompatibilityCheck#697 does not need to wait for roave/better-reflection
changes