Repository: Roave/BetterReflection (php)

Issue:
Add accessors to UnableToCompileNode exception
For a project @mstaab and I are working on it would be useful if we could fetch the name of a constant when an `Roave\BetterReflection\NodeCompiler\Exception\UnableToCompileNode` exception is thrown:

https://github.com/Roave/BetterReflection/blob/0095a8df72ff99e1b785418e97688ff259108694/src/NodeCompiler/Exception/UnableToCompileNode.php#L44-L54

Currently we use a regular expression to analyze the exception message, retrieve the name of the constant, then define the constant, and retry the operation. 

Before I open a pull request, I wanted to inquire if you were open for 

- adding accessors to `UnableToCompileNode`
- extracting dedicated exceptions extending `UnableToCompileNode` (for backwards compatibility) and adding accessors there

What do you think?

Pull request:
Enhancement: Allow fetching constant name from exception
This PR

* [x] allows fetching the constant name from an `UnableToCompileNode` exception

Fixes #569.

Patch:
diff --git a/src/NodeCompiler/Exception/UnableToCompileNode.php b/src/NodeCompiler/Exception/UnableToCompileNode.php
index 3cad9874..b2510e0d 100644
--- a/src/NodeCompiler/Exception/UnableToCompileNode.php
+++ b/src/NodeCompiler/Exception/UnableToCompileNode.php
@@ -15,6 +15,14 @@ use function sprintf;
 
 class UnableToCompileNode extends LogicException
 {
+    /** @var string|null */
+    private $constantName;
+
+    public function constantName() : ?string
+    {
+        return $this->constantName;
+    }
+
     public static function forUnRecognizedExpressionInContext(Node\Expr $expression, CompilerContext $context) : self
     {
         return new self(sprintf(
@@ -45,12 +53,18 @@ class UnableToCompileNode extends LogicException
         CompilerContext $fetchContext,
         Node\Expr\ConstFetch $constantFetch
     ) : self {
-        return new self(sprintf(
+        $constantName = reset($constantFetch->name->parts);
+
+        $exception = new self(sprintf(
             'Could not locate constant "%s" while evaluating expression in %s at line %s',
-            reset($constantFetch->name->parts),
+            $constantName,
             self::compilerContextToContextDescription($fetchContext),
             $constantFetch->getLine()
         ));
+
+        $exception->constantName = $constantName;
+
+        return $exception;
     }
 
     private static function compilerContextToContextDescription(CompilerContext $fetchContext) : string


Test patch:
diff --git a/test/unit/NodeCompiler/Exception/UnableToCompileNodeTest.php b/test/unit/NodeCompiler/Exception/UnableToCompileNodeTest.php
index 8cccd3db..6252a0dd 100644
--- a/test/unit/NodeCompiler/Exception/UnableToCompileNodeTest.php
+++ b/test/unit/NodeCompiler/Exception/UnableToCompileNodeTest.php
@@ -17,24 +17,42 @@ use Roave\BetterReflection\Reflection\ReflectionClass;
 use Roave\BetterReflection\Reflector\ClassReflector;
 use Roave\BetterReflection\SourceLocator\Type\StringSourceLocator;
 use Roave\BetterReflectionTest\BetterReflectionSingleton;
+use function sprintf;
 
 /**
  * @covers \Roave\BetterReflection\NodeCompiler\Exception\UnableToCompileNode
  */
 final class UnableToCompileNodeTest extends TestCase
 {
+    public function testDefaults() : void
+    {
+        $exception = new UnableToCompileNode();
+
+        self::assertNull($exception->constantName());
+    }
+
     /** @dataProvider supportedContextTypes */
     public function testBecauseOfNotFoundConstantReference(CompilerContext $context) : void
     {
+        $constantName = 'FOO';
+
+        $exception = UnableToCompileNode::becauseOfNotFoundConstantReference(
+            $context,
+            new ConstFetch(new Name($constantName))
+        );
+
         $contextName = $context->hasSelf() ? 'EmptyClass' : 'unknown context (probably a function)';
 
         self::assertSame(
-            'Could not locate constant "FOO" while evaluating expression in ' . $contextName . ' at line -1',
-            UnableToCompileNode::becauseOfNotFoundConstantReference(
-                $context,
-                new ConstFetch(new Name('FOO'))
-            )->getMessage()
+            sprintf(
+                'Could not locate constant "%s" while evaluating expression in %s at line -1',
+                $constantName,
+                $contextName
+            ),
+            $exception->getMessage()
         );
+
+        self::assertSame($constantName, $exception->constantName());
     }
 
     /** @dataProvider supportedContextTypes */