{"record":{"id":"fa52ae93696f8d85","repo":"rectorphp/rector","slug":"trying-to-replace-statement-s-with-expression","errorCode":null,"errorMessage":"Trying to replace statement (%s) with expression (%s). Are you missing a Stmt_Expression wrapper?","messagePattern":"Trying to replace statement \\((.+?)\\) with expression \\((.+?)\\)\\. Are you missing a Stmt_Expression wrapper\\?","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/PhpParser/NodeTraverser/RectorNodeTraverser.php","lineNumber":252,"sourceCode":"            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;\n    }\n    private function ensureReplacementReasonable(Node $old, Node $new): void\n    {\n        if ($old instanceof Stmt) {\n            if ($new instanceof Expr) {\n                throw new LogicException(sprintf('Trying to replace statement (%s) ', $old->getType()) . sprintf('with expression (%s). Are you missing a ', $new->getType()) . 'Stmt_Expression wrapper?');\n            }\n            return;\n        }\n        if ($new instanceof Stmt) {\n            throw new LogicException(sprintf('Trying to replace expression (%s) ', $old->getType()) . sprintf('with statement (%s)', $new->getType()));\n        }\n    }\n    /**\n     * This must happen after $this->configuration is set after ProcessCommand::execute() is run, otherwise we get default false positives.\n     *\n     * This should be removed after https://github.com/rectorphp/rector/issues/5584 is resolved\n     */\n    private function prepareNodeVisitors(): void\n    {\n        if ($this->areNodeVisitorsPrepared) {\n            return;\n        }\n        // filter out by PHP version","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/src/PhpParser/NodeTraverser/RectorNodeTraverser.php#L234-L270","documentation":"ensureReplacementReasonable() runs before any replacement is committed: if the old node is a Stmt and the new one is a bare Expr, PHP-Parser's AST could not represent it (statements must sit at statement positions), so Rector throws LogicException and hints at the missing Stmt wrapper — PhpParser\\Node\\Stmt\\Expression.","triggerScenarios":"A rule matched on a statement (e.g. Expression, Echo_, If_) has its refactor() return an Expr such as new MethodCall(...) or new Assign(...) without wrapping; the message prints the concrete types, e.g. 'Trying to replace statement (Stmt_Expression) with expression (Expr_MethodCall)'.","commonSituations":"Downgrading a statement into a call (replacing an if with a function call); rules migrated from older Rector where the check was looser; returning the inner expression of a wrapped node by mistake.","solutions":["Wrap the expression: return new \\PhpParser\\Node\\Stmt\\Expression($expr);","Or return an array of statements if you produce several","Double-check you matched the right node level: match Expression and return a new Expression, not the inner Expr"],"exampleFix":"// before: $node is Stmt_Expression\nreturn new MethodCall($node->expr, 'configure');\n\n// after\nuse PhpParser\\Node\\Stmt\\Expression;\n\nreturn new Expression(new MethodCall($node->expr, 'configure'));","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"use PhpParser\\Node;\nuse PhpParser\\Node\\Stmt;\nuse PhpParser\\Node\\Stmt\\Expression;\n\n/** Coerce a replacement to the level of the node it replaces. */\nfunction toSameNodeLevel(Node $old, Node $new): Node\n{\n    if ($old instanceof Stmt && ! $new instanceof Stmt && $new instanceof \\PhpParser\\Node\\Expr) {\n        return new Expression($new);\n    }\n    return $new;\n}","tryCatchPattern":null,"preventionTips":["Match and return at the same AST level (Expression in, Expression out)","Wrap generated expressions with new Expression(...) the moment you create them for statement slots","Add fixture tests covering the exact statement type your rule rewrites"],"tags":["php","rector","php-parser","ast","node-replacement"],"backgroundTag":"invalid-node-replacement","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}