{"record":{"id":"6b81b5c3bfc5b195","repo":"rectorphp/rector","slug":"replace-with-null-can-not-be-used-if-the-parent-st","errorCode":null,"errorMessage":"REPLACE_WITH_NULL can not be used if the parent structure is an array","messagePattern":"REPLACE_WITH_NULL can not be used if the parent structure is an array","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/PhpParser/NodeTraverser/RectorNodeTraverser.php","lineNumber":228,"sourceCode":"                            // stop traversing as node type changed and visitors won't work\n                            continue 2;\n                        }\n                    } elseif (\\is_array($return)) {\n                        $doNodes[] = [$i, $return];\n                        continue 2;\n                    } elseif ($return === NodeVisitor::REMOVE_NODE) {\n                        $doNodes[] = [$i, []];\n                        continue 2;\n                    } elseif ($return === NodeVisitor::DONT_TRAVERSE_CHILDREN) {\n                        $traverseChildren = \\false;\n                    } elseif ($return === NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {\n                        $traverseChildren = \\false;\n                        break;\n                    } elseif ($return === NodeVisitor::STOP_TRAVERSAL) {\n                        $this->stopTraversal = \\true;\n                        break 2;\n                    } elseif ($return === NodeVisitor::REPLACE_WITH_NULL) {\n                        throw new LogicException('REPLACE_WITH_NULL can not be used if the parent structure is an array');\n                    } else {\n                        throw new LogicException('enterNode() returned invalid value of type ' . gettype($return));\n                    }\n                }\n            }\n            if ($traverseChildren) {\n                $this->traverseNode($node);\n                if ($this->stopTraversal) {\n                    break;\n                }\n            }\n        }\n        if ($doNodes !== []) {\n            while ([$i, $replace] = array_pop($doNodes)) {\n                array_splice($nodes, $i, 1, $replace);\n            }\n        }\n        return $nodes;","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/src/PhpParser/NodeTraverser/RectorNodeTraverser.php#L210-L246","documentation":"Inside traverseArray(), NodeVisitor::REPLACE_WITH_NULL is meaningless: the node sits at an integer offset of a node array, not on an object property that could be nulled. Rector throws LogicException immediately and requires REMOVE_NODE, which splices the offset out of the array instead.","triggerScenarios":"An enterNode()/leaveNode() visitor returns NodeVisitor::REPLACE_WITH_NULL for a node whose parent container is an array — typically a statement inside ClassMethod->stmts or an argument inside FuncCall->args.","commonSituations":"Porting a php-parser visitor that used null returns on property-backed nodes; writing a rule that deletes statements and picking the wrong sentinel constant by autocomplete.","solutions":["Return NodeVisitor::REMOVE_NODE to delete a node from a statement/argument array","In Rector rules, prefer having refactor() return NodeVisitor::REMOVE_NODE instead of hand-rolled visitor constants","Reserve REPLACE_WITH_NULL for nodes stored on object properties (e.g. a single default value)"],"exampleFix":"// before: node lives inside $classMethod->stmts[]\nif ($shouldDelete) {\n    return NodeVisitor::REPLACE_WITH_NULL;\n}\n\n// after\nif ($shouldDelete) {\n    return NodeVisitor::REMOVE_NODE;\n}","handlingStrategy":"validation","validationCode":"// pick the removal sentinel by parent container, up front\nuse PhpParser\\NodeVisitor;\n\n$removalSentinel = $parentNode instanceof \\PhpParser\\Node\\Stmt\\Expression\n    ? NodeVisitor::REMOVE_NODE   // node sits in a stmts[] array\n    : NodeVisitor::REMOVE_NODE;  // arrays and properties both use REMOVE_NODE; null only via property assignment\n// REPLACE_WITH_NULL is never valid from enterNode on array children: never select it there.","typeGuard":null,"tryCatchPattern":"try {\n    $traverser->traverse([$node]);\n} catch (\\LogicException $e) {\n    // visitor contract bug: inspect which sentinel the offending visitor returned\n    $this->fail($e->getMessage());\n}","preventionTips":["Default to NodeVisitor::REMOVE_NODE for deletions; it works in every context","Let REMOVE_WITH_NULL stay an implementation detail of property-backed replacement in Rector's traverser, not your return value","In Rector rules, return the constant from refactor() and let AbstractRector translate"],"tags":["php","rector","php-parser","node-visitor","remove-node"],"backgroundTag":"invalid-node-visitor-return","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}