mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 23:04:59 +00:00

RecursiveASTVisitor was recursing into the subtrees of an old root if it was changed in on_entry callback. Fix that by querying root pointer just after on_entry callback returns. While on it, also use `AK::TemporaryChange` instead of setting `m_current_subtree_pointer` manually. As it turns out, `FunctionCallCanonicalizationPass` was relying on being able to replace tree on entry, and the bug in RecursiveASTVisitor made the pass to not fully canonicalize nested function calls. The changes to GenericASTPass.cpp alone are enough to fix the problem but it is canonical (for some definition of canonicity) to only change trees in on_leave. Therefore, the commit also switches FunctionCallCanonicalizationPass to on_leave callback. A test for this fix and one from the previous commit is also included.
61 lines
1.4 KiB
C++
61 lines
1.4 KiB
C++
/*
|
|
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/TemporaryChange.h>
|
|
|
|
#include "AST/AST.h"
|
|
#include "Compiler/GenericASTPass.h"
|
|
#include "Function.h"
|
|
|
|
namespace JSSpecCompiler {
|
|
|
|
void RecursiveASTVisitor::run_in_const_subtree(NullableTree nullable_tree)
|
|
{
|
|
if (nullable_tree) {
|
|
auto tree = nullable_tree.release_nonnull();
|
|
auto tree_copy = tree;
|
|
NodeSubtreePointer pointer { &tree };
|
|
recurse(tree, pointer);
|
|
VERIFY(tree == tree_copy);
|
|
}
|
|
}
|
|
|
|
void RecursiveASTVisitor::run_in_subtree(Tree& tree)
|
|
{
|
|
NodeSubtreePointer pointer { &tree };
|
|
recurse(tree, pointer);
|
|
}
|
|
|
|
void RecursiveASTVisitor::replace_current_node_with(NullableTree tree)
|
|
{
|
|
m_current_subtree_pointer->replace_subtree({}, move(tree));
|
|
}
|
|
|
|
RecursionDecision RecursiveASTVisitor::recurse(Tree root, NodeSubtreePointer& pointer)
|
|
{
|
|
TemporaryChange change { m_current_subtree_pointer, &pointer };
|
|
|
|
RecursionDecision decision = on_entry(root);
|
|
root = pointer.get({});
|
|
|
|
if (decision == RecursionDecision::Recurse) {
|
|
for (auto& child : root->subtrees()) {
|
|
if (recurse(child.get({}), child) == RecursionDecision::Break)
|
|
return RecursionDecision::Break;
|
|
}
|
|
}
|
|
|
|
on_leave(root);
|
|
|
|
return RecursionDecision::Continue;
|
|
}
|
|
|
|
void GenericASTPass::process_function()
|
|
{
|
|
run_in_subtree(m_function->m_ast);
|
|
}
|
|
|
|
}
|