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

1# use clang to load and walk a compilation database 

2import textwrap 

3from collections.abc import Iterable 

4 

5from more_itertools import last 

6 

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 

20 

21example_1 = textwrap.dedent(""" 

22#include <vector> 

23struct Size 

24{ 

25 double length; 

26 double width; 

27 

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) {} 

31 

32 Size size() 

33 { 

34 return Size(this->length, this->width); 

35 } 

36}; 

37 

38typedef const char* string; 

39 

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}; 

55 

56class ListView_LEGACY{ 

57 public: 

58 ListView_LEGACY(); 

59 ListView_LEGACY(string container, int val); 

60 Size size; 

61}; 

62 

63ListView_LEGACY::ListView_LEGACY(string container, int val){ 

64 

65} 

66ListView_LEGACY::ListView_LEGACY(){ 

67 

68} 

69 

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}; 

77 

78void derived::another_func(int s){ 

79 int a = 0; 

80 // another_func 0 

81 // another_func 1 

82} 

83 

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} 

91 

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} 

100 

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} 

128 

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} 

150 

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__(){} 

166 

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} 

189 

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""") 

210 

211 

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 

218 

219 

220class MyRefactor: 

221 def __init__(self): 

222 self._calls = [] 

223 

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) 

267 

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()) 

284 

285 

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() 

290 

291 

292if __name__ == "__main__": 

293 batch_recipe_example()