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