Skip to content

Commit

Permalink
Bleeding edge - use lighter NodeConnectingVisitor
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 4, 2022
1 parent 9484ae0 commit 2418b2d
Show file tree
Hide file tree
Showing 24 changed files with 434 additions and 203 deletions.
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ parameters:
explicitMixedInUnknownGenericNew: true
arrayFilter: true
arrayUnpacking: true
nodeConnectingVisitorCompatibility: false
stubFiles:
- ../stubs/bleedingEdge/Countable.stub
29 changes: 29 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ parameters:
explicitMixedInUnknownGenericNew: false
arrayFilter: false
arrayUnpacking: false
nodeConnectingVisitorCompatibility: true
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down Expand Up @@ -215,6 +216,7 @@ parametersSchema:
explicitMixedInUnknownGenericNew: bool(),
arrayFilter: bool(),
arrayUnpacking: bool(),
nodeConnectingVisitorCompatibility: bool(),
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down Expand Up @@ -344,6 +346,8 @@ conditionalTags:
phpstan.rules.rule: %exceptions.check.tooWideThrowType%
PHPStan\Rules\Exceptions\TooWideMethodThrowTypeRule:
phpstan.rules.rule: %exceptions.check.tooWideThrowType%
PhpParser\NodeVisitor\NodeConnectingVisitor:
phpstan.parser.richParserNodeVisitor: %featureToggles.nodeConnectingVisitorCompatibility%

services:
-
Expand All @@ -358,6 +362,31 @@ services:
options:
preserveOriginalNames: true

-
class: PHPStan\Parser\ArrayFilterArgVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\ArrayMapArgVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\NewAssignedToPropertyVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\ParentStmtTypesVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\TryCatchTypeVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PhpParser\NodeVisitor\NodeConnectingVisitor

Expand Down
43 changes: 8 additions & 35 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -1642,32 +1642,11 @@ private function resolveType(Expr $node): Type
}

$callableParameters = null;
$arg = $node->getAttribute('parent');
if ($arg instanceof Arg) {
$funcCall = $arg->getAttribute('parent');
$argOrder = $arg->getAttribute('expressionOrder');
if ($funcCall instanceof FuncCall && $funcCall->name instanceof Name) {
$functionName = $this->reflectionProvider->resolveFunctionName($funcCall->name, $this);
if (
$functionName === 'array_map'
&& $argOrder === 0
&& isset($funcCall->getArgs()[1])
) {
if (!isset($funcCall->getArgs()[2])) {
$callableParameters = [
new DummyParameter('item', $this->getType($funcCall->getArgs()[1]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
];
} else {
$callableParameters = [];
foreach ($funcCall->getArgs() as $i => $funcCallArg) {
if ($i === 0) {
continue;
}

$callableParameters[] = new DummyParameter('item', $this->getType($funcCallArg->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null);
}
}
}
$arrayMapArgs = $node->getAttribute('arrayMapArgs');
if ($arrayMapArgs !== null) {
$callableParameters = [];
foreach ($arrayMapArgs as $funcCallArg) {
$callableParameters[] = new DummyParameter('item', $this->getType($funcCallArg->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null);
}
}

Expand Down Expand Up @@ -5745,14 +5724,8 @@ private function exactInstantiation(New_ $node, string $className): ?Type
return $objectType;
}

$parentNode = $node->getAttribute('parent');
if (
(
$parentNode instanceof Expr\Assign
|| $parentNode instanceof Expr\AssignRef
)
&& $parentNode->var instanceof PropertyFetch
) {
$assignedToProperty = $node->getAttribute('assignedToProperty');
if ($assignedToProperty !== null) {
$constructorVariant = ParametersAcceptorSelector::selectSingle($constructorMethod->getVariants());
$classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes();
$originalClassTemplateTypes = $classTemplateTypes;
Expand All @@ -5771,7 +5744,7 @@ private function exactInstantiation(New_ $node, string $className): ?Type
}

if (count($classTemplateTypes) === count($originalClassTemplateTypes)) {
$propertyType = $this->getType($parentNode->var);
$propertyType = $this->getType($assignedToProperty);
if ($objectType->isSuperTypeOf($propertyType)->yes()) {
return $propertyType;
}
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/ConditionalTagsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Nette\Schema\Expect;
use PHPStan\Analyser\TypeSpecifierFactory;
use PHPStan\Broker\BrokerFactory;
use PHPStan\Parser\RichParser;
use PHPStan\PhpDoc\TypeNodeResolverExtension;
use PHPStan\Rules\RegistryFactory;
use PHPStan\ShouldNotHappenException;
Expand All @@ -31,6 +32,7 @@ public function getConfigSchema(): Nette\Schema\Schema
TypeSpecifierFactory::FUNCTION_TYPE_SPECIFYING_EXTENSION_TAG => $bool,
TypeSpecifierFactory::METHOD_TYPE_SPECIFYING_EXTENSION_TAG => $bool,
TypeSpecifierFactory::STATIC_METHOD_TYPE_SPECIFYING_EXTENSION_TAG => $bool,
RichParser::VISITOR_SERVICE_TAG => $bool,
])->min(1));
}

Expand Down
25 changes: 25 additions & 0 deletions src/Parser/ArrayFilterArgVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class ArrayFilterArgVisitor extends NodeVisitorAbstract
{

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name) {
$functionName = $node->name->toLowerString();
if ($functionName === 'array_filter') {
$args = $node->getArgs();
if (isset($args[0])) {
$args[0]->setAttribute('isArrayFilterArg', $args);
}
}
}
return null;
}

}
30 changes: 30 additions & 0 deletions src/Parser/ArrayMapArgVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use function array_slice;
use function count;

class ArrayMapArgVisitor extends NodeVisitorAbstract
{

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name) {
$functionName = $node->name->toLowerString();
if ($functionName === 'array_map') {
$args = $node->getArgs();
if (isset($args[0])) {
$slicedArgs = array_slice($args, 1);
if (count($slicedArgs) > 0) {
$args[0]->value->setAttribute('arrayMapArgs', $slicedArgs);
}
}
}
}
return null;
}

}
21 changes: 21 additions & 0 deletions src/Parser/NewAssignedToPropertyVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class NewAssignedToPropertyVisitor extends NodeVisitorAbstract
{

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignRef) {
if ($node->var instanceof Node\Expr\PropertyFetch && $node->expr instanceof Node\Expr\New_) {
$node->expr->setAttribute('assignedToProperty', $node->var);
}
}
return null;
}

}
48 changes: 48 additions & 0 deletions src/Parser/ParentStmtTypesVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use function array_pop;
use function count;
use function get_class;

final class ParentStmtTypesVisitor extends NodeVisitorAbstract
{

/** @var array<int, class-string<Node\Stmt|Node\Expr\Closure>> */
private array $typeStack = [];

public function beforeTraverse(array $nodes): ?array
{
$this->typeStack = [];
return null;
}

public function enterNode(Node $node): ?Node
{
if (!$node instanceof Node\Stmt && !$node instanceof Node\Expr\Closure) {
return null;
}

if (count($this->typeStack) > 0) {
$node->setAttribute('parentStmtTypes', $this->typeStack);
}
$this->typeStack[] = get_class($node);

return null;
}

public function leaveNode(Node $node): ?Node
{
if (!$node instanceof Node\Stmt && !$node instanceof Node\Expr\Closure) {
return null;
}

array_pop($this->typeStack);

return null;
}

}
14 changes: 8 additions & 6 deletions src/Parser/RichParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitor\NodeConnectingVisitor;
use PHPStan\DependencyInjection\Container;
use PHPStan\File\FileReader;
use PHPStan\NodeVisitor\StatementOrderVisitor;
use PHPStan\ShouldNotHappenException;
use function is_string;
use function strpos;
Expand All @@ -20,12 +19,13 @@
class RichParser implements Parser
{

public const VISITOR_SERVICE_TAG = 'phpstan.parser.richParserNodeVisitor';

public function __construct(
private \PhpParser\Parser $parser,
private Lexer $lexer,
private NameResolver $nameResolver,
private NodeConnectingVisitor $nodeConnectingVisitor,
private StatementOrderVisitor $statementOrderVisitor,
private Container $container,
)
{
}
Expand Down Expand Up @@ -60,8 +60,10 @@ public function parseString(string $sourceCode): array

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor($this->nameResolver);
$nodeTraverser->addVisitor($this->nodeConnectingVisitor);
$nodeTraverser->addVisitor($this->statementOrderVisitor);

foreach ($this->container->getServicesByTag(self::VISITOR_SERVICE_TAG) as $visitor) {
$nodeTraverser->addVisitor($visitor);
}

/** @var array<Node\Stmt> */
$nodes = $nodeTraverser->traverse($nodes);
Expand Down
73 changes: 73 additions & 0 deletions src/Parser/TryCatchTypeVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use function array_pop;
use function array_reverse;
use function count;

final class TryCatchTypeVisitor extends NodeVisitorAbstract
{

/** @var array<int, array<int, string>|null> */
private array $typeStack = [];

public function beforeTraverse(array $nodes): ?array
{
$this->typeStack = [];
return null;
}

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Stmt || $node instanceof Node\Expr\Match_) {
if (count($this->typeStack) > 0) {
$node->setAttribute('tryCatchTypes', $this->typeStack[count($this->typeStack) - 1]);
}
}

if ($node instanceof Node\FunctionLike) {
$this->typeStack[] = null;
}

if ($node instanceof Node\Stmt\TryCatch) {
$types = [];
foreach (array_reverse($this->typeStack) as $stackTypes) {
if ($stackTypes === null) {
break;
}

foreach ($stackTypes as $type) {
$types[] = $type;
}
}
foreach ($node->catches as $catch) {
foreach ($catch->types as $type) {
$types[] = $type->toString();
}
}

$this->typeStack[] = $types;
}

return null;
}

public function leaveNode(Node $node): ?Node
{
if (
!$node instanceof Node\Stmt\TryCatch
&& !$node instanceof Node\FunctionLike
&& !$node instanceof Node\Expr\Match_
) {
return null;
}

array_pop($this->typeStack);

return null;
}

}
Loading

0 comments on commit 2418b2d

Please sign in to comment.