From 9db45c919199c68126ba3183c0276db0ca23fff8 Mon Sep 17 00:00:00 2001 From: Daniel Leech Date: Wed, 7 Feb 2024 11:08:15 +0000 Subject: [PATCH] Support reflecting node context and function context arguments --- .../Reflector/TolerantSourceCodeReflector.php | 9 +++++++++ .../Inference/Context/FunctionCallContext.php | 20 +++++++++++++++++-- src/Core/Inference/FunctionArguments.php | 5 +++++ .../Resolver/CallExpressionResolver.php | 6 ++++-- .../Resolver/QualifiedNameResolver.php | 2 +- .../Inference/Walker/TestAssertWalker.php | 5 ++++- src/Core/Reflector/CompositeReflector.php | 7 +++++++ src/Core/Reflector/CoreReflector.php | 7 +++++++ .../ContextualSourceCodeReflector.php | 7 +++++++ src/Core/Reflector/SourceCodeReflector.php | 4 ++++ 10 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php b/src/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php index 42bbc2c5..35a956d3 100644 --- a/src/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php +++ b/src/Bridge/TolerantParser/Reflector/TolerantSourceCodeReflector.php @@ -4,12 +4,14 @@ use Amp\Promise; use Generator; +use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Node\SourceFileNode; use Phpactor\WorseReflection\Bridge\TolerantParser\Reflection\ReflectionNavigation; use Phpactor\WorseReflection\Core\Diagnostic; use Phpactor\WorseReflection\Core\Diagnostics; use Phpactor\WorseReflection\Core\Exception\CouldNotResolveNode; use Phpactor\WorseReflection\Core\Exception\MethodCallNotFound; +use Phpactor\WorseReflection\Core\Inference\NodeContext; use Phpactor\WorseReflection\Core\Inference\Walker; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionDeclaredConstantCollection; use Phpactor\WorseReflection\Core\Reflection\ReflectionNode; @@ -125,6 +127,13 @@ public function navigate(TextDocument $sourceCode): ReflectionNavigation return new ReflectionNavigation($this->serviceLocator, $this->parseSourceCode($sourceCode)); } + public function reflectNodeContext(Node $node): NodeContext + { + $frame = $this->serviceLocator->frameBuilder()->build($node); + return $this->serviceLocator->nodeContextResolver()->resolveNode($frame, $node); + } + + public function reflectNode( TextDocument $sourceCode, ByteOffset|int $offset diff --git a/src/Core/Inference/Context/FunctionCallContext.php b/src/Core/Inference/Context/FunctionCallContext.php index ce7cfd6c..04341c01 100644 --- a/src/Core/Inference/Context/FunctionCallContext.php +++ b/src/Core/Inference/Context/FunctionCallContext.php @@ -3,6 +3,7 @@ namespace Phpactor\WorseReflection\Core\Inference\Context; use Phpactor\TextDocument\ByteOffsetRange; +use Phpactor\WorseReflection\Core\Inference\FunctionArguments; use Phpactor\WorseReflection\Core\Inference\NodeContext; use Phpactor\WorseReflection\Core\Inference\Symbol; use Phpactor\WorseReflection\Core\Name; @@ -14,6 +15,7 @@ public function __construct( Symbol $symbol, private ByteOffsetRange $byteOffsetRange, private ReflectionFunction $function, + private FunctionArguments $arguments, ) { parent::__construct( $symbol, @@ -31,8 +33,22 @@ public function function(): ReflectionFunction return $this->function; } - public static function create(Name $name, ByteOffsetRange $byteOffsetRange, ReflectionFunction $function): self + public function arguments(): FunctionArguments { - return new self(Symbol::fromTypeNameAndPosition(Symbol::FUNCTION, $name, $byteOffsetRange), $byteOffsetRange, $function); + return $this->arguments; + } + + public static function create( + Name $name, + ByteOffsetRange $byteOffsetRange, + ReflectionFunction $function, + FunctionArguments $arguments, + ): self { + return new self( + Symbol::fromTypeNameAndPosition(Symbol::FUNCTION, $name, $byteOffsetRange), + $byteOffsetRange, + $function, + $arguments, + ); } } diff --git a/src/Core/Inference/FunctionArguments.php b/src/Core/Inference/FunctionArguments.php index a3a0c2d8..fac6fea1 100644 --- a/src/Core/Inference/FunctionArguments.php +++ b/src/Core/Inference/FunctionArguments.php @@ -38,6 +38,11 @@ public static function fromList(NodeContextResolver $resolver, Frame $frame, ?Ar ))); } + public function has(int $index): bool + { + return isset($this->arguments[$index]); + } + public function at(int $index): NodeContext { if (!isset($this->arguments[$index])) { diff --git a/src/Core/Inference/Resolver/CallExpressionResolver.php b/src/Core/Inference/Resolver/CallExpressionResolver.php index 6efebad1..65656dc2 100644 --- a/src/Core/Inference/Resolver/CallExpressionResolver.php +++ b/src/Core/Inference/Resolver/CallExpressionResolver.php @@ -91,16 +91,18 @@ private function processConditionalType( if ($context->symbol()->symbolType() === Symbol::FUNCTION) { $function = $resolver->reflector()->reflectFunction($context->symbol()->name()); + $arguments = FunctionArguments::fromList($resolver, $frame, $node->argumentExpressionList); return (new FunctionCallContext( $context->symbol(), ByteOffsetRange::fromInts( $node->getStartPosition(), $node->getEndPosition() ), - $function + $function, + $arguments, ))->withType($type->evaluate( $function, - FunctionArguments::fromList($resolver, $frame, $node->argumentExpressionList) + $arguments )); } diff --git a/src/Core/Inference/Resolver/QualifiedNameResolver.php b/src/Core/Inference/Resolver/QualifiedNameResolver.php index 34390951..52680af6 100644 --- a/src/Core/Inference/Resolver/QualifiedNameResolver.php +++ b/src/Core/Inference/Resolver/QualifiedNameResolver.php @@ -136,7 +136,7 @@ private function resolveContextFromCall( $function = VirtualReflectionFunction::empty($name, $range); } - $context = FunctionCallContext::create($name, $range, $function); + $context = FunctionCallContext::create($name, $range, $function, FunctionArguments::fromList($resolver, $frame, $parent->argumentExpressionList)); $stub = $this->registry->get($name->short()); diff --git a/src/Core/Inference/Walker/TestAssertWalker.php b/src/Core/Inference/Walker/TestAssertWalker.php index 1a70c8af..5ef1f72a 100644 --- a/src/Core/Inference/Walker/TestAssertWalker.php +++ b/src/Core/Inference/Walker/TestAssertWalker.php @@ -196,16 +196,19 @@ private function resolveArgs(?ArgumentExpressionList $argList, FrameResolver $re private function assertTypeIs(Node $node, Type $actualType, Type $expectedType, ?NodeContext $message = null): void { $message = isset($message) ? TypeUtil::valueOrNull($message->type()) : null; + $position = PositionConverter::intByteOffsetToPosition($node->getStartPosition(), $node->getFileContents()); if ($actualType->__toString() === TypeUtil::valueOrNull($expectedType)) { $this->testCase->addToAssertionCount(1); return; } $this->testCase->fail(sprintf( - "%s: \n\n %s\n\nis:\n\n %s\n\non offset", + "%s: \n\n %s\n\nis:\n\n %s\n\non offset %s line %s char %s", $message ?: 'Failed asserting that:', $actualType->__toString(), trim($expectedType->__toString(), '"'), $node->getStartPosition(), + $position->line + 1, + $position->character + 1, )); } } diff --git a/src/Core/Reflector/CompositeReflector.php b/src/Core/Reflector/CompositeReflector.php index 2ff2d099..d72d859d 100644 --- a/src/Core/Reflector/CompositeReflector.php +++ b/src/Core/Reflector/CompositeReflector.php @@ -4,7 +4,9 @@ use Amp\Promise; use Generator; +use Microsoft\PhpParser\Node; use Phpactor\WorseReflection\Bridge\TolerantParser\Reflection\ReflectionNavigation; +use Phpactor\WorseReflection\Core\Inference\NodeContext; use Phpactor\WorseReflection\Core\Inference\Walker; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionDeclaredConstantCollection; use Phpactor\WorseReflection\Core\Reflection\ReflectionDeclaredConstant; @@ -102,6 +104,11 @@ public function diagnostics(TextDocument $sourceCode): Promise return $this->sourceCodeReflector->diagnostics($sourceCode); } + public function reflectNodeContext(Node $node): NodeContext + { + return $this->sourceCodeReflector->reflectNodeContext($node); + } + public function reflectNode(TextDocument $sourceCode, $offset): ReflectionNode { return $this->sourceCodeReflector->reflectNode($sourceCode, $offset); diff --git a/src/Core/Reflector/CoreReflector.php b/src/Core/Reflector/CoreReflector.php index 01ab2acc..6c576df8 100644 --- a/src/Core/Reflector/CoreReflector.php +++ b/src/Core/Reflector/CoreReflector.php @@ -4,6 +4,7 @@ use Amp\Promise; use Generator; +use Microsoft\PhpParser\Node; use Phpactor\TextDocument\ByteOffset; use Phpactor\WorseReflection\Bridge\TolerantParser\Reflection\ReflectionNavigation; use Phpactor\WorseReflection\Core\ClassName; @@ -12,6 +13,7 @@ use Phpactor\WorseReflection\Core\Exception\CycleDetected; use Phpactor\WorseReflection\Core\Exception\FunctionNotFound; use Phpactor\WorseReflection\Core\Exception\NotFound; +use Phpactor\WorseReflection\Core\Inference\NodeContext; use Phpactor\WorseReflection\Core\Inference\Walker; use Phpactor\WorseReflection\Core\Name; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionDeclaredConstantCollection; @@ -247,6 +249,11 @@ public function diagnostics(TextDocument $sourceCode): Promise return $this->sourceReflector->diagnostics($sourceCode); } + public function reflectNodeContext(Node $node): NodeContext + { + return $this->sourceReflector->reflectNodeContext($node); + } + public function reflectNode($sourceCode, $offset): ReflectionNode { return $this->sourceReflector->reflectNode($sourceCode, $offset); diff --git a/src/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php b/src/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php index 30f37f48..f29b5959 100644 --- a/src/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php +++ b/src/Core/Reflector/SourceCode/ContextualSourceCodeReflector.php @@ -4,9 +4,11 @@ use Amp\Promise; use Generator; +use Microsoft\PhpParser\Node; use Phpactor\TextDocument\TextDocument; use Phpactor\TextDocument\TextDocumentBuilder; use Phpactor\WorseReflection\Bridge\TolerantParser\Reflection\ReflectionNavigation; +use Phpactor\WorseReflection\Core\Inference\NodeContext; use Phpactor\WorseReflection\Core\Inference\Walker; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionDeclaredConstantCollection; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionFunctionCollection; @@ -86,4 +88,9 @@ public function walk(TextDocument $sourceCode, Walker $walker): Generator { return $this->innerReflector->walk($sourceCode, $walker); } + + public function reflectNodeContext(Node $node): NodeContext + { + return $this->innerReflector->reflectNodeContext($node); + } } diff --git a/src/Core/Reflector/SourceCodeReflector.php b/src/Core/Reflector/SourceCodeReflector.php index 10e64420..c7fc9d02 100644 --- a/src/Core/Reflector/SourceCodeReflector.php +++ b/src/Core/Reflector/SourceCodeReflector.php @@ -4,12 +4,14 @@ use Amp\Promise; use Generator; +use Microsoft\PhpParser\Node; use Phpactor\TextDocument\ByteOffset; use Phpactor\TextDocument\TextDocument; use Phpactor\WorseReflection\Bridge\TolerantParser\Reflection\ReflectionNavigation; use Phpactor\WorseReflection\Core\Diagnostic; use Phpactor\WorseReflection\Core\Diagnostics; use Phpactor\WorseReflection\Core\Inference\Frame; +use Phpactor\WorseReflection\Core\Inference\NodeContext; use Phpactor\WorseReflection\Core\Inference\Walker; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionClassLikeCollection; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionDeclaredConstantCollection; @@ -64,6 +66,8 @@ public function reflectNode( ByteOffset|int $offset ): ReflectionNode; + public function reflectNodeContext(Node $node): NodeContext; + public function reflectConstantsIn( TextDocument $sourceCode ): ReflectionDeclaredConstantCollection;