<commit_before><?php

namespace Phpactor\WorseReflection\Core\Inference;

use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\Node\Expression\ArrayCreationExpression;
use Microsoft\PhpParser\Node\Expression\CallExpression;
use Microsoft\PhpParser\Node\Expression\MemberAccessExpression;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\Expression\SubscriptExpression;
use Microsoft\PhpParser\Node\Expression\Variable as ParserVariable;
use Microsoft\PhpParser\Node\NamespaceUseClause;
use Microsoft\PhpParser\Node\NumericLiteral;
use Microsoft\PhpParser\Node\Parameter;
use Microsoft\PhpParser\Node\QualifiedName;
use Microsoft\PhpParser\Node\ReservedWord;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
use Microsoft\PhpParser\Node\Statement\InterfaceDeclaration;
use Microsoft\PhpParser\Node\StringLiteral;
use Microsoft\PhpParser\Token;
use Phpactor\WorseReflection\Core\Logger;
use Phpactor\WorseReflection\Reflector;
use Phpactor\WorseReflection\Core\Type;
use Microsoft\PhpParser\Node\Expression\ScopedPropertyAccessExpression;
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
use Microsoft\PhpParser\Node\Expression\TernaryExpression;
use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\ClassLike;
use Microsoft\PhpParser\Node\PropertyDeclaration;
use Microsoft\PhpParser\Node\ConstElement;

class SymbolInformationResolver
{
    /**
     * @var MemberTypeResolver
     */
    private $memberTypeResolver;

    /**
     * @var Reflector
     */
    private $reflector;

    /**
     * @var Logger
     */
    private $logger;

    /**
     * @var SymbolFactory
     */
    private $symbolFactory;

    public function __construct(Reflector $reflector, Logger $logger, SymbolFactory $symbolFactory = null)
    {
        $this->logger = $logger;
        $this->symbolFactory = $symbolFactory ?: new SymbolFactory();
        $this->memberTypeResolver = new MemberTypeResolver($reflector, $logger, $this->symbolFactory);
    }

    /**
    ace
     */
    public function resolveNode(Frame $frame, $node): SymbolInformation
    {
        return $this->_resolveNode($frame, $node);
    }

    /**
     * Internal interface
     */
    public function _resolveNode(Frame $frame, $node): SymbolInformation
    {
        if (false === $node instanceof Node) {
            $this->logger->warning(sprintf('Non-node class passed to resolveNode, got "%s"', get_class($node)));
            return SymbolInformation::none();
        }

        return $this->__resolveNode($frame, $node);
    }

    private function __resolveNode(Frame $frame, Node $node): SymbolInformation
    {
        $this->logger->debug(sprintf('Resolving: %s', get_class($node)));

        if ($node instanceof QualifiedName) {
            return $this->symbolFactory->information($node, [
                'type' => $this->resolveQualifiedNameType($node),
                'symbol_type' => Symbol::CLASS_,
            ]);
        }

        if ($node instanceof ConstElement) {
            return $this->symbolFactory->information($node, [
                'token' => $node->name,
                'symbol_type' => Symbol::CONSTANT,
                'container_type' => $this->classTypeFromNode($node)
            ]);
        }

        if ($node instanceof Parameter) {
            return $this->resolveParameter($frame, $node);
        }

        if ($node instanceof ParserVariable) {
            return $this->resolveVariable($frame, $node);
        }

        if ($node instanceof MemberAccessExpression) {
            return $this->resolveMemberAccessExpression($frame, $node);
        }

        if ($node instanceof CallExpression) {
            return $this->resolveCallExpression($frame, $node);
        }

        if ($node instanceof ScopedPropertyAccessExpression) {
            return $this->resolveScopedPropertyAccessExpression($frame, $node);
        }

        if ($node instanceof ClassDeclaration || $node instanceof InterfaceDeclaration) {
            return $this->symbolFactory->information($node, [
                'token' => $node->name,
                'symbol_type' => Symbol::CLASS_,
                'type' => Type::fromString($node->getNamespacedName())
            ]);
        }

        if ($node instanceof ObjectCreationExpression) {
            return $this->resolveObjectCreationExpression($frame, $node);
        }

        if ($node instanceof SubscriptExpression) {
            $variableValue = $this->_resolveNode($frame, $node->postfixExpression);
            return $this->resolveSubscriptExpression($frame, $variableValue, $node);
        }

        if ($node instanceof StringLiteral) {
            return $this->symbolFactory->information(
                $node,
                [
                    'symbol_type' => Symbol::STRING,
                    'type' => Type::string(),
                    'value' => (string) $node->getStringContentsText(),
                    'container_type' => $this->classTypeFromNode($node)
                ]
            );
        }

        if ($node instanceof NumericLiteral) {
            return $this->resolveNumericLiteral($node);
        }

        if ($node instanceof ReservedWord) {
            return $this->resolveReservedWord($node);
        }

        if ($node instanceof ArrayCreationExpression) {
            return $this->resolveArrayCreationExpression($frame, $node);
        }

        if ($node instanceof ArgumentExpression) {
            return $this->_resolveNode($frame, $node->expression);
        }

        if ($node instanceof TernaryExpression) {
            return $this->resolveTernaryExpression($frame, $node);
        }

        if ($node instanceof MethodDeclaration) {
            return $this->resolveMethodDeclaration($frame, $node);
        }

        $this->logger->warning(sprintf(
            'Did not know how to resolve node of type "%s" with text "%s"',
            get_class($node),
            $node->getText()
        ));

        return SymbolInformation::none();
    }

    private function resolveVariable(Frame $frame, ParserVariable $node)
    {
        if ($node->getFirstAncestor(PropertyDeclaration::class)) {
            return $this->resolvePropertyVariable($node);
        }

        $name = $node->getText();
        $offset = $node->getFullStart();
        $variables = $frame->locals()->lessThanOrEqualTo($offset)->byName($name);

        if (0 === $variables->count()) {
            return SymbolInformation::none();
        }

        return $variables->last()->symbolInformation();
    }

    private function resolvePropertyVariable(ParserVariable $node)
    {
        $info = $this->symbolFactory->information($node, [
            'symbol_type' => Symbol::PROPERTY,
            'token' => $node->name,
        ]);

        return $this->memberTypeResolver->propertyType(
            $this->classTypeFromNode($node),
            $info,
            $info->symbol()->name()
        );
    }

    private function resolveMemberAccessExpression(Frame $frame, MemberAccessExpression $node): SymbolInformation
    {
        $class = $this->_resolveNode($frame, $node->dereferencableExpression);

        return $this->_valueFromMemberAccess($frame, $class->type(), $node);<commit_msg>Return symbol type for variable not in frame<commit_after><?php

namespace Phpactor\WorseReflection\Core\Inference;

use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\Node\Expression\ArrayCreationExpression;
use Microsoft\PhpParser\Node\Expression\CallExpression;
use Microsoft\PhpParser\Node\Expression\MemberAccessExpression;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\Expression\SubscriptExpression;
use Microsoft\PhpParser\Node\Expression\Variable as ParserVariable;
use Microsoft\PhpParser\Node\NamespaceUseClause;
use Microsoft\PhpParser\Node\NumericLiteral;
use Microsoft\PhpParser\Node\Parameter;
use Microsoft\PhpParser\Node\QualifiedName;
use Microsoft\PhpParser\Node\ReservedWord;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
use Microsoft\PhpParser\Node\Statement\InterfaceDeclaration;
use Microsoft\PhpParser\Node\StringLiteral;
use Microsoft\PhpParser\Token;
use Phpactor\WorseReflection\Core\Logger;
use Phpactor\WorseReflection\Reflector;
use Phpactor\WorseReflection\Core\Type;
use Microsoft\PhpParser\Node\Expression\ScopedPropertyAccessExpression;
use Microsoft\PhpParser\Node\Expression\ArgumentExpression;
use Microsoft\PhpParser\Node\Expression\TernaryExpression;
use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\ClassLike;
use Microsoft\PhpParser\Node\PropertyDeclaration;
use Microsoft\PhpParser\Node\ConstElement;

class SymbolInformationResolver
{
    /**
     * @var MemberTypeResolver
     */
    private $memberTypeResolver;

    /**
     * @var Reflector
     */
    private $reflector;

    /**
     * @var Logger
     */
    private $logger;

    /**
     * @var SymbolFactory
     */
    private $symbolFactory;

    public function __construct(Reflector $reflector, Logger $logger, SymbolFactory $symbolFactory = null)
    {
        $this->logger = $logger;
        $this->symbolFactory = $symbolFactory ?: new SymbolFactory();
        $this->memberTypeResolver = new MemberTypeResolver($reflector, $logger, $this->symbolFactory);
    }

    /**
    ace
     */
    public function resolveNode(Frame $frame, $node): SymbolInformation
    {
        return $this->_resolveNode($frame, $node);
    }

    /**
     * Internal interface
     */
    public function _resolveNode(Frame $frame, $node): SymbolInformation
    {
        if (false === $node instanceof Node) {
            $this->logger->warning(sprintf('Non-node class passed to resolveNode, got "%s"', get_class($node)));
            return SymbolInformation::none();
        }

        return $this->__resolveNode($frame, $node);
    }

    private function __resolveNode(Frame $frame, Node $node): SymbolInformation
    {
        $this->logger->debug(sprintf('Resolving: %s', get_class($node)));

        if ($node instanceof QualifiedName) {
            return $this->symbolFactory->information($node, [
                'type' => $this->resolveQualifiedNameType($node),
                'symbol_type' => Symbol::CLASS_,
            ]);
        }

        if ($node instanceof ConstElement) {
            return $this->symbolFactory->information($node, [
                'token' => $node->name,
                'symbol_type' => Symbol::CONSTANT,
                'container_type' => $this->classTypeFromNode($node)
            ]);
        }

        if ($node instanceof Parameter) {
            return $this->resolveParameter($frame, $node);
        }

        if ($node instanceof ParserVariable) {
            return $this->resolveVariable($frame, $node);
        }

        if ($node instanceof MemberAccessExpression) {
            return $this->resolveMemberAccessExpression($frame, $node);
        }

        if ($node instanceof CallExpression) {
            return $this->resolveCallExpression($frame, $node);
        }

        if ($node instanceof ScopedPropertyAccessExpression) {
            return $this->resolveScopedPropertyAccessExpression($frame, $node);
        }

        if ($node instanceof ClassDeclaration || $node instanceof InterfaceDeclaration) {
            return $this->symbolFactory->information($node, [
                'token' => $node->name,
                'symbol_type' => Symbol::CLASS_,
                'type' => Type::fromString($node->getNamespacedName())
            ]);
        }

        if ($node instanceof ObjectCreationExpression) {
            return $this->resolveObjectCreationExpression($frame, $node);
        }

        if ($node instanceof SubscriptExpression) {
            $variableValue = $this->_resolveNode($frame, $node->postfixExpression);
            return $this->resolveSubscriptExpression($frame, $variableValue, $node);
        }

        if ($node instanceof StringLiteral) {
            return $this->symbolFactory->information(
                $node,
                [
                    'symbol_type' => Symbol::STRING,
                    'type' => Type::string(),
                    'value' => (string) $node->getStringContentsText(),
                    'container_type' => $this->classTypeFromNode($node)
                ]
            );
        }

        if ($node instanceof NumericLiteral) {
            return $this->resolveNumericLiteral($node);
        }

        if ($node instanceof ReservedWord) {
            return $this->resolveReservedWord($node);
        }

        if ($node instanceof ArrayCreationExpression) {
            return $this->resolveArrayCreationExpression($frame, $node);
        }

        if ($node instanceof ArgumentExpression) {
            return $this->_resolveNode($frame, $node->expression);
        }

        if ($node instanceof TernaryExpression) {
            return $this->resolveTernaryExpression($frame, $node);
        }

        if ($node instanceof MethodDeclaration) {
            return $this->resolveMethodDeclaration($frame, $node);
        }

        $this->logger->warning(sprintf(
            'Did not know how to resolve node of type "%s" with text "%s"',
            get_class($node),
            $node->getText()
        ));

        return SymbolInformation::none();
    }

    private function resolveVariable(Frame $frame, ParserVariable $node)
    {
        if ($node->getFirstAncestor(PropertyDeclaration::class)) {
            return $this->resolvePropertyVariable($node);
        }

        $name = $node->getText();
        $offset = $node->getFullStart();
        $variables = $frame->locals()->lessThanOrEqualTo($offset)->byName($name);

        if (0 === $variables->count()) {
            return $this->symbolFactory->information($node, [
                'symbol_type' => Symbol::VARIABLE,
            ]);
        }

        return $variables->last()->symbolInformation();
    }

    private function resolvePropertyVariable(ParserVariable $node)
    {
        $info = $this->symbolFactory->information($node, [
            'symbol_type' => Symbol::PROPERTY,
            'token' => $node->name,
        ]);

        return $this->memberTypeResolver->propertyType(
            $this->classTypeFromNode($node),
            $info,
            $info->symbol()->name()
        );
    }

    private function resolveMemberAccessExpression(Frame $frame, MemberAccessExpression $node): SymbolInformation
    {
        $class = $this->_resolveNode($frame, $node->dereferencableExpression);

        return $this->_valueFromMemberAccess($frame, $class->type(), $node);
{'fraction_non_alphanumeric': 0.12235485621269669, 'fraction_numerical': 0.00013564839934888768, 'mean_word_length': 2.659717051377513, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 166, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7550927', 'n_tokens_mistral': 3826, 'n_tokens_neox': 3738, 'n_words': 726}