> */ private const BREAK_NODES = [FunctionLike::class, ClassMethod::class]; /** * @var array> */ private const LOOP_NODES = [For_::class, Foreach_::class, While_::class, Do_::class, Switch_::class]; /** * @var BetterNodeFinder */ private $betterNodeFinder; /** * @var NodeTypeResolver */ private $nodeTypeResolver; public function __construct(BetterNodeFinder $betterNodeFinder, NodeTypeResolver $nodeTypeResolver) { $this->betterNodeFinder = $betterNodeFinder; $this->nodeTypeResolver = $nodeTypeResolver; } public function isInLoop(Node $node): bool { $stopNodes = array_merge(self::LOOP_NODES, self::BREAK_NODES); $firstParent = $this->betterNodeFinder->findParentTypes($node, $stopNodes); if (! $firstParent instanceof Node) { return false; } foreach (self::LOOP_NODES as $type) { if (is_a($firstParent, $type, true)) { return true; } } return false; } public function isInIf(Node $node): bool { $breakNodes = array_merge([If_::class], self::BREAK_NODES); $previousNode = $this->betterNodeFinder->findParentTypes($node, $breakNodes); if (! $previousNode instanceof Node) { return false; } return $previousNode instanceof If_; } public function isHasAssignWithIndirectReturn(Node $node, If_ $if): bool { $loopNodes = self::LOOP_NODES; foreach ($loopNodes as $loopNode) { $loopObjectType = new ObjectType($loopNode); $parentType = $this->nodeTypeResolver->resolve($node); $superType = $parentType->isSuperTypeOf($loopObjectType); $isLoopType = $superType->yes(); if (! $isLoopType) { continue; } $next = $node->getAttribute(AttributeKey::NEXT_NODE); if ($next instanceof Node) { if ($next instanceof Return_ && $next->expr === null) { continue; } $hasAssign = (bool) $this->betterNodeFinder->findInstanceOf($if->stmts, Assign::class); if (! $hasAssign) { continue; } return true; } } return false; } }