Coverage for src/rejuvenation/recipe_example.py: 31%
54 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-02 13:12 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-02 13:12 +0000
1"""AI: Example script demonstrating a C++ refactoring recipe applied across a Clang compilation database."""
3# use clang to load and walk a compilation database
4import textwrap
5from collections.abc import Iterable
7from more_itertools import last
9from renaissance.integrations.clang import ClangASTNode, CPPPatternFactory
10from renaissance.integrations.clang.clang_json_ast_node import ClangJsonASTNode
11from renaissance.integrations.clang.predicates import is_clang_constructor, is_clang_method, is_clang_type_reference
12from renaissance.syntax_tree import (
13 ASTFactory,
14 ASTNode,
15 ASTProcessor,
16 ASTRefactorActions,
17 RecipeASTProcessor,
18 TextUtils,
19 recipe_step,
20)
21from renaissance.syntax_tree.ast_finder import matches_node
23example_1 = textwrap.dedent("""
24#include <vector>
25struct Size
26{
27 double length;
28 double width;
30 // Constructor to initialize the Rectangle object with length and width
31 Size() : length(0), width(0) {}
32 Size(double len, double wid) : length(len), width(wid) {}
34 Size size()
35 {
36 return Size(this->length, this->width);
37 }
38};
40typedef const char* string;
42int main(){
43 // do nothing
44}
45void setItemLayout(int, Size size){
46}
47class aClass{
48 void main1(std::vector<int> m_items){
49 std::vector<int> idToBeReplaced;
50 idToBeReplaced.push_back((int)m_items.size());
51 setItemLayout(1, Size(this->getBounds().size().width, 30));
52 }
53 Size getBounds(){
54 return Size(10, 30);
55 }
56};
58class ListView_LEGACY{
59 public:
60 ListView_LEGACY();
61 ListView_LEGACY(string container, int val);
62 Size size;
63};
65ListView_LEGACY::ListView_LEGACY(string container, int val){
67}
68ListView_LEGACY::ListView_LEGACY(){
70}
72class derived : public ListView_LEGACY{
73 public:
74 derived(string cont) : ListView_LEGACY(cont, 5) {
75 // something
76 };
77 void another_func(int s);
78};
80void derived::another_func(int s){
81 int a = 0;
82 // another_func 0
83 // another_func 1
84}
86void main2(string container){
87 /* hahaha*/
88 ListView_LEGACY listview(container, 3);
89 int b;
90 int a;
91 listview.size = Size(4, 5);
92}
94void main3()
95{
96 int b;
97 string container, foo;
98 ListView_LEGACY listview(container, 3);
99 derived d(foo);
100 listview.size = Size(4, 5);
101}
103void main4(std::vector<int> m_items)
104{
105 /**
106 * multi-line comments
107 * in my code;
108 * do this wrack my indent algo?
109 */
110 std::vector<int> idToBeReplaced;
111 /**
112 * multi-line comments
113 * in my code;
114 * do this wrack my indent algo?
115 */
116 idToBeReplaced.push_back((int)m_items.size());
117 /**
118 * multi-line comments
119 * in my code;
120 * do this wrack my indent algo?
121 */
122}
123""")
124expected_output = textwrap.dedent("""
125 void main(){
126 std::vector<int> NEW_ID;
127 NEW_ID.push_back((int)m_items.size());
128 setItemLayout(1, Size(this->getBounds().size().width,30));
129}
131void main() {
132 /*ahah*/
133 ListViewCustom listview;
134 ListViewHeader listviewHeader0
135 /* Conversion note: give header appropriate name */
136 ListViewHeader listviewHeader1
137 /* Conversion note: give header appropriate name */
138 ListViewHeader listviewHeader2 /* Conversion note: give header appropriate name */
139 bool b; bool a;
140 listview(container),
141 listviewHeader0(listview),
142 listviewHeader1(listview),
143 listviewHeader2(listview);
144 listview.size = Size(4, 5);
145 listviewHeader0.name = L"listviewHeader0";/* Conversion note: give header appropriate name */
146 listviewHeader0.size = Size(256, 30); /* Conversion note: provide correct sizes */
147 listviewHeader1.name = L"listviewHeader1";/* Conversion note: give header appropriate name */
148 listviewHeader1.size = Size(256, 30); /* Conversion note: provide correct sizes */
149 listviewHeader2.name = L"listviewHeader2";/* Conversion note: give header appropriate name */
150 listviewHeader2.size = Size(256, 30); /* Conversion note: provide correct sizes */
151}
153class ListView_LEGACY {
154 ListView_LEGACY(string container, int val);
155};
156class derived: public ListView_LEGACY {
157 derived(string cont):ListViewCustom(cont), m_headers {,
158 std:make_unique<ListViewHeader>(*this),
159 std:make_unique<ListViewHeader>(*this),
160 std:make_unique<ListViewHeader>(*this),
161 std:make_unique<ListViewHeader>(*this),
162 std:make_unique<ListViewHeader>(*this)}{
163 //something
164 };
165 void another_func(int s );
166};
167void __REPLACEMENT__(){}
169void main(){
170 ListViewCustom listview;
171 ListViewHeader listviewHeader0
172 /* Conversion note: give header appropriate name */
173 ListViewHeader listviewHeader1
174 /* Conversion note: give header appropriate name */
175 ListViewHeader listviewHeader2 /* Conversion note: give header appropriate name */
176 bool b;
177 string container;
178 listview(container),
179 listviewHeader0(listview),
180 listviewHeader1(listview),
181 listviewHeader2(listview);
182 derived d();
183 listview.size = Size(4, 5);
184 listviewHeader0.name = L"listviewHeader0";/* Conversion note: give header appropriate name */
185 listviewHeader0.size = Size(256, 30); /* Conversion note: provide correct sizes */
186 listviewHeader1.name = L"listviewHeader1";/* Conversion note: give header appropriate name */
187 listviewHeader1.size = Size(256, 30); /* Conversion note: provide correct sizes */
188 listviewHeader2.name = L"listviewHeader2";/* Conversion note: give header appropriate name */
189 listviewHeader2.size = Size(256, 30); /* Conversion note: provide correct sizes */
190}
192void main(){
193 /**
194 * multi-line comments
195 * in my code;
196 * do this wrack my indent algo?
197 */
198 std::vector<int> NEW_ID;
199 /**
200 * multi-line comments
201 * in my code;
202 * do this wrack my indent algo?
203 */
204 NEW_ID.push_back((int)m_items.size());
205 /**
206 * multi-line comments
207 * in my code;
208 * do this wrack my indent algo?
209 */
210}
211""")
214# generate a simple code base provider in real life use a compilation database
215def simple_codebase_provider() -> Iterable[tuple[ASTFactory, ASTNode]]:
216 """AI: Yield a (factory, ATU) pair for the example C++ snippet using the Clang integration."""
217 for impl_type in [ClangASTNode, ClangJsonASTNode][0:1]:
218 factory = ASTFactory(impl_type)
219 atu1 = factory.create_from_text(example_1, impl_type.__name__ + "1.cpp")
220 yield factory, atu1
223class MyRefactor:
224 """AI: Example recipe demonstrating text/name/declaration replacement via ASTRefactorActions."""
226 def __init__(self):
227 """AI: Initialize an empty recipe for collecting refactoring actions."""
228 self._calls = []
230 @recipe_step(order=0)
231 def recipe(self, ast_processor: ASTProcessor):
232 """AI: Apply the example text, name, and declaration replacements to the processed AST."""
233 pattern = CPPPatternFactory(ast_processor.factory)
234 actions = ASTRefactorActions(ast_processor, pattern)
235 actions.replace_text("ListView_LEGACY", "ListViewCustom", skip_kind=is_clang_type_reference)
236 actions.replace_name("another_func", "__REPLACEMENT__", is_clang_method)
237 actions.replace_text("idToBeReplaced", "NEW_ID")
238 # TODO debate the way to replace this the options are:
239 # 1. make a match of the consecutive nodes.
240 # 2. find a neat construction for the current backtick replacement
241 actions.replace_declaration("int $var;", r"bool $var`int\s+(.+)`;")
242 # create a constructor pattern
243 constructor_pattern = pattern.create(
244 "typedef int string; class ListView_LEGACY { ListView_LEGACY(string container, int val); };",
245 kind="Constructor",
246 )
247 # create a pattern to match a call to a constructor in both declarations and derived classes
248 constructor_call_pattern = pattern.create_constructor_call("$var($container, $headerCount)")
249 # search for the constructor pattern
250 for constructor_match in ast_processor.find_match(constructor_pattern):
251 # and then search for the referenced by calls to the constructor
252 for constructor_call in constructor_match.match_referenced_by([constructor_call_pattern]):
253 var_node = constructor_call.nodes["$var"][0]
254 parent = var_node.parent
255 assert isinstance(parent, ASTNode), f"{parent} is not an ASTNode"
256 header_count = int(constructor_call.expansions["$headerCount"])
257 # remove the count argument from the constructor call
258 # TODO it would be a lot easier if ast rewrite would support removal of the second argument
259 # but currently (I guess) that would lead to a dangling comma
260 # TODO the items between the backtick represent a regex where all groups are the used replacements
261 # this might need some investigation what is the best way to handle this
262 if matches_node(parent, is_clang_constructor):
263 # remove constructor header count argument
264 ast_processor.replace(r"ListViewCustom($container)", constructor_call)
265 repl = ",\n ".join("std:make_unique<ListViewHeader>(*this)" for _ in range(header_count))
266 ast_processor.insert_after(", m_headers {" + repl + "}", constructor_call, True, False)
267 else:
268 var = parent.name
269 container = constructor_call.expansions["$container"]
270 # replace the constructor call with a ListViewCustom object
271 ast_processor.replace(f"ListViewCustom {var}({container});", parent)
272 # find reference to the declaration
273 size_match = last(ref.node.get_ancestor("Call_?Expr") for ref in parent.referenced_by)
275 for h in range(header_count):
276 ast_processor.insert_after(
277 f"\n/* Conversion note: give header appropriate name */\nListViewHeader listviewHeader{h}({var});",
278 parent,
279 True,
280 False,
281 )
282 if size_match:
283 text = TextUtils.strip_indent(f"""
284 listviewHeader{h}.name = L"listviewHeader{h}";/* Conversion note: give header appropriate name */
285 listviewHeader{h}.size = Size(256, 30); /* Conversion note: provide correct sizes */
286 """)
287 ast_processor.insert_after(text, size_match, True, False)
288 # for idx, line in enumerate(ast_processor.apply_to_string().split('\n')):
289 # print(f'{idx+1}: {line}')
290 TextUtils.to_clipboard(ast_processor.apply_to_string())
293def batch_recipe_example():
294 """AI: Run `MyRefactor` over the example codebase and copy the rewritten source to the clipboard."""
295 print("example batch analysis using recipe:\n")
296 recipe_ast_processor = RecipeASTProcessor(MyRefactor(), simple_codebase_provider, r".*", in_memory=True)
297 recipe_ast_processor.run()
300if __name__ == "__main__":
301 batch_recipe_example()