Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
35cb4d0
ExpressionResultStorage
ondrejmirtes Dec 5, 2025
66e4f13
VirtualAssignNodeCallback + ShallowNodeCallback
ondrejmirtes Dec 10, 2025
3d9a776
Fix double processing of match with enum condition
ondrejmirtes Dec 11, 2025
0f03b30
First class callable name was not processed
ondrejmirtes Dec 11, 2025
edb1a6c
Prevent double processing of `StaticCall::$name`
ondrejmirtes Dec 11, 2025
3beb8c6
Fix match analysis
ondrejmirtes Dec 11, 2025
141c443
Add currentlyAssignedExpressions and currentlyAllowedUndefinedExpress…
ondrejmirtes Dec 11, 2025
7d51e73
Arg value inherited currentlyAllowedUndefinedExpressions because of a…
ondrejmirtes Dec 11, 2025
719a973
GNSR is dead, long live FNSR
ondrejmirtes Dec 3, 2025
040e9ce
FNSR preparation
ondrejmirtes Dec 11, 2025
2073668
FNSR
ondrejmirtes Dec 11, 2025
3d361fb
Do not change what `$expr` is so that it's added to the storage
ondrejmirtes Dec 15, 2025
283136a
Fix cache in LegacyNodeScopeResolverTest
ondrejmirtes Dec 15, 2025
d83934e
Issue bot - use FNSR
ondrejmirtes Dec 15, 2025
6a28cf0
Fix
ondrejmirtes Dec 16, 2025
707f363
Keep errors ordering even when nodeCallback executed out of order
ondrejmirtes Dec 16, 2025
088c69c
toFiberScope, toMutatingScope - take advantage of inheritance and pol…
ondrejmirtes Dec 17, 2025
80d12db
Introduce DeepNodeCloner
ondrejmirtes Dec 17, 2025
8708b07
Fix NullsafePropertyFetch and NullsafeMethodCall with FiberScope
ondrejmirtes Dec 17, 2025
2604680
Missing printer methods for BooleanOrNode and BooleanAndNode
ondrejmirtes Dec 17, 2025
7b4c7b9
Fix LegacyNodeScopeResolverTest with trait uses
ondrejmirtes Dec 17, 2025
f4e17d0
Fix Coalesce with FiberScope
ondrejmirtes Dec 17, 2025
0223f74
Fix Isset and Empty with FiberScope
ondrejmirtes Dec 17, 2025
dbf5200
Fix storing results of first class callable expressions
ondrejmirtes Dec 17, 2025
01efece
Fix FiberScope with dynamic variable names
ondrejmirtes Dec 17, 2025
6583bdd
Fix duplicate Assign result storing
ondrejmirtes Dec 18, 2025
4147d7e
Do not pile up too many fibers for synthetic nodes
ondrejmirtes Dec 18, 2025
7a04260
Autowire FiberNodeScopeResolver where NodeScopeResolver is expected (…
ondrejmirtes Dec 18, 2025
b0bc22b
Fix
ondrejmirtes Dec 18, 2025
239f07e
Add internal `toMutatingScope` on `Scope` interface
ondrejmirtes Dec 19, 2025
5a285c8
NodeScopeResolver and MutatingScope can be non-final
ondrejmirtes Dec 19, 2025
2226259
Revert "Issue bot - use FNSR"
ondrejmirtes Dec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
GNSR is dead, long live FNSR
  • Loading branch information
ondrejmirtes committed Dec 17, 2025
commit 719a97308c0470ee595375ccf59d18f7261c0af8
9 changes: 8 additions & 1 deletion src/Testing/RuleTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Analyser\Analyser;
use PHPStan\Analyser\AnalyserResultFinalizer;
use PHPStan\Analyser\Error;
use PHPStan\Analyser\Fiber\FiberNodeScopeResolver;
use PHPStan\Analyser\FileAnalyser;
use PHPStan\Analyser\Generator\GeneratorNodeScopeResolver;
use PHPStan\Analyser\Generator\GeneratorScopeFactory;
Expand Down Expand Up @@ -97,7 +98,13 @@ protected function createNodeScopeResolver(): NodeScopeResolver|GeneratorNodeSco
$reflectionProvider = $this->createReflectionProvider();
$typeSpecifier = $this->getTypeSpecifier();

return new NodeScopeResolver(
$enableFnsr = getenv('PHPSTAN_FNSR');
$className = NodeScopeResolver::class;
if ($enableFnsr === '1') {
$className = FiberNodeScopeResolver::class;
}

return new $className(
$reflectionProvider,
self::getContainer()->getByType(InitializerExprTypeResolver::class),
self::getReflector(),
Expand Down
9 changes: 8 additions & 1 deletion src/Testing/TypeInferenceTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Fiber\FiberNodeScopeResolver;
use PHPStan\Analyser\Generator\GeneratorNodeScopeResolver;
use PHPStan\Analyser\Generator\GeneratorScopeFactory;
use PHPStan\Analyser\Generator\NodeHandler\VarAnnotationHelper;
Expand Down Expand Up @@ -71,7 +72,13 @@ protected static function createNodeScopeResolver(): NodeScopeResolver|Generator
$reflectionProvider = self::createReflectionProvider();
$typeSpecifier = $container->getService('typeSpecifier');

return new NodeScopeResolver(
$enableFnsr = getenv('PHPSTAN_FNSR');
$className = NodeScopeResolver::class;
if ($enableFnsr === '1') {
$className = FiberNodeScopeResolver::class;
}

return new $className(
$reflectionProvider,
$container->getByType(InitializerExprTypeResolver::class),
self::getReflector(),
Expand Down
109 changes: 109 additions & 0 deletions tests/PHPStan/Analyser/Fiber/FiberNodeScopeResolverRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser\Fiber;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\VerbosityLevel;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\RequiresPhp;

/**
* @extends RuleTestCase<Rule<Node>>
*/
#[RequiresPhp('>= 8.1')]
class FiberNodeScopeResolverRuleTest extends RuleTestCase
{

/** @var callable(Node, Scope): list<IdentifierRuleError> */
private $ruleCallback;

protected function getRule(): Rule
{
return new class ($this->ruleCallback) implements Rule {

/**
* @param callable(Node, Scope): list<IdentifierRuleError> $ruleCallback
*/
public function __construct(private $ruleCallback)
{
}

public function getNodeType(): string
{
return Node::class;
}

public function processNode(Node $node, Scope $scope): array
{
return ($this->ruleCallback)($node, $scope);
}

};
}

public static function dataRule(): iterable
{
yield [
static fn (Node $node, Scope $scope) => [],
[],
];
yield [
static function (Node $node, Scope $scope) {
if (!$node instanceof Node\Expr\MethodCall) {
return [];
}

$arg0 = $scope->getType($node->getArgs()[0]->value);
$arg0 = $scope->getType($node->getArgs()[0]->value); // on purpose to hit the cache

return [
RuleErrorBuilder::message($arg0->describe(VerbosityLevel::precise()))->identifier('gnsr.rule')->build(),
RuleErrorBuilder::message($scope->getType($node->getArgs()[1]->value)->describe(VerbosityLevel::precise()))->identifier('gnsr.rule')->build(),
RuleErrorBuilder::message($scope->getType($node->getArgs()[2]->value)->describe(VerbosityLevel::precise()))->identifier('gnsr.rule')->build(),
];
},
[
['1', 21],
['2', 21],
['3', 21],
],
];
yield [
static function (Node $node, Scope $scope) {
if (!$node instanceof Node\Expr\MethodCall) {
return [];
}

$synthetic = $scope->getType(new Node\Scalar\String_('foo'));
$synthetic2 = $scope->getType(new Node\Scalar\String_('bar'));

return [
RuleErrorBuilder::message($synthetic->describe(VerbosityLevel::precise()))->identifier('gnsr.rule')->build(),
RuleErrorBuilder::message($synthetic2->describe(VerbosityLevel::precise()))->identifier('gnsr.rule')->build(),
];
},
[
['\'foo\'', 21],
['\'bar\'', 21],
],
];
}

/**
* @param callable(Node, Scope): list<IdentifierRuleError> $ruleCallback
* @param list<array{0: string, 1: int, 2?: string|null}> $expectedErrors
* @return void
*/
#[DataProvider('dataRule')]
public function testRule(callable $ruleCallback, array $expectedErrors): void
{
$this->ruleCallback = $ruleCallback;
$this->analyse([__DIR__ . '/data/rule.php'], $expectedErrors);
}

}
38 changes: 38 additions & 0 deletions tests/PHPStan/Analyser/Fiber/FiberNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser\Fiber;

use PHPStan\Testing\TypeInferenceTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\RequiresPhp;

#[RequiresPhp('>= 8.1')]
class FiberNodeScopeResolverTest extends TypeInferenceTestCase
{

public static function dataFileAsserts(): iterable
{
yield from self::gatherAssertTypes(__DIR__ . '/data/gnsr.php');
}

/**
* @param mixed ...$args
*/
#[DataProvider('dataFileAsserts')]
public function testFileAsserts(
string $assertType,
string $file,
...$args,
): void
{
$this->assertFileAsserts($assertType, $file, ...$args);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/../../../../conf/bleedingEdge.neon',
];
}

}
Loading