Coverage for src / rejuvenation / batch_process_examples.py: 94%
66 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 Callable, Iterable
4from dataclasses import dataclass
6from renaissance.integrations.clang import ClangASTNode
7from renaissance.integrations.clang.clang_json_ast_node import ClangJsonASTNode
8from renaissance.integrations.types import Call
9from renaissance.recipes import CleanupRefactoring
10from renaissance.syntax_tree import (
11 ASTFactory,
12 ASTNode,
13 ASTProcessor,
14 BatchASTProcessor,
15 TextUtils,
16)
17from renaissance.syntax_tree.recipe_ast_processor import (
18 RecipeASTProcessor,
19 after_step,
20 final_action,
21 recipe_step,
22)
24example_1 = textwrap.dedent("""
25 void x(int a) {}
26 void x1(int a) {}
27 void x2(int a) {}
29 void f1(int a){
30 int unused = 0;
31 int unused2 = 0; //must be removed
32 if (a==1) {
33 int unused = 0;
34 int unused2 = 0; //should be kept
35 int c = unused2;
36 x1(c);
37 }
38 }
39 """)
41example_2 = textwrap.dedent("""
42 void x(int a) {}
43 void x1(int a) {}
44 void x2(int a) {}
45 void f2(int a){
46 int unused = 0;
47 if (a==1) {
48 int unused = 0;
49 int another_unused = 0;
50 int used2 = 0; //should be kept
51 int c = used2;
52 x2(c);
53 }
54 }
55 """)
58# generate a simple code base provider in real life use a compilation database
59def simple_codebase_provider() -> Iterable[tuple[ASTFactory, ASTNode]]:
60 for impl_type in [ClangASTNode, ClangJsonASTNode]:
61 factory = ASTFactory(impl_type)
62 atu1 = factory.create_from_text(example_1, impl_type.__name__ + "1.c")
63 yield factory, atu1
64 atu2 = factory.create_from_text(example_2, impl_type.__name__ + "2.c")
65 yield factory, atu2
68def print_results(title, batch_processor):
69 print(title + ":")
70 for file, code in batch_processor.in_memory_files.items():
71 print(TextUtils.shift_right(file, 4) + "\n")
72 print(TextUtils.shift_right(code, 8) + "\n")
75def batch_remove_unused_variable_once_example():
76 """This function demonstrates a batch processing example using different AST node implementations.
77 It iterates over a list of AST node implementations (`ClangASTNode` and `ClangJsonASTNode`),
78 and for each implementation, it generates a codebase provider that yields tuples of
79 `ASTFactory` and `ASTNode` created from example source texts (`example_1` and `example_2`).
80 The function then creates a `BatchASTProcessor` with in-memory storage enabled and processes
81 the codebase using the `CleanupRefactoring.remove_unused_variables` refactoring operation.
82 Finally, it prints the rewritten code stored in memory.
83 """
84 # generate a batch processor for testing purposes we store into memory
85 batch_processor = BatchASTProcessor(in_memory=True)
86 batch_processor.once(simple_codebase_provider, CleanupRefactoring.remove_unused_variables)
87 # print the rewritten code normally you would write to a file
88 print_results("example batch remove unused variable once", batch_processor)
91def batch_repeat_example():
92 """Demonstrates the use of a batch processor to perform multiple refactoring operations on a codebase.
93 This example creates an in-memory batch processor and applies two refactoring operations:
94 1. CleanupRefactoring.remove_unused_variables: Removes unused variables from the codebase.
95 2. remove_function: Removes all function calls from the codebase.
96 The results of the refactoring operations are printed to the console.
98 Repeat is in action here:
99 the first time the codebase is processed, the unused variables are removed.
100 and the function calls are removed.
101 the second time the codebase is processed, the new unused variables are removed again.
103 Note:
104 In a real-world scenario, the rewritten code would typically be written to a file instead of being printed.
106 """
107 # generate a batch processor for testing purposes we store into memory
108 batch_processor = BatchASTProcessor(in_memory=True)
110 # remove a function to create more unused variables
111 def remove_function(ast_processor: ASTProcessor):
112 [ast_processor.insert_before("// ", node, False, False) for node in ast_processor.find_ast_type(Call)]
114 # batch_processor.repeat(simple_codebase_provider, [remove_function])
115 batch_processor.repeat(
116 simple_codebase_provider,
117 [CleanupRefactoring.remove_unused_variables, remove_function],
118 )
119 # print the rewritten code normally you would write to a file
120 print_results("example batch repeat", batch_processor)
123@dataclass
124class CallInfo:
125 callee: str
126 calls: str
129class AnalysisRecipe:
130 def __init__(self):
131 self._calls = []
133 @recipe_step(order=0)
134 def store_function_call(self, ast_processor: ASTProcessor) -> Callable[[], None] | None:
135 # find all function calls and store them, this routing is invoked in parallel!
136 calls: list[CallInfo] = []
137 [AnalysisRecipe._add_function_call(node, calls) for node in ast_processor.find_ast_type(Call)]
138 # the resulting lambda is invoked single threaded
139 # this kind of mechanism is mainly used to store results from multiple processors
140 # for refactoring operations this is not needed as a refactoring operation is single threaded
141 if calls:
142 return lambda: self._calls.extend(calls)
143 return None
145 @after_step("store_function_call")
146 def just_show_the_method(self):
147 print("called after store_function_call")
149 @final_action()
150 def final_action(self):
151 print("Calls:")
152 for call in self._calls:
153 print(" " + call.callee + " -- calls --> " + call.calls)
155 @staticmethod
156 def _add_function_call(call: ASTNode, calls: list[CallInfo]):
157 callee = call.get_ancestor("(?i)Function_?Decl")
158 if callee:
159 calls.append(CallInfo(callee.name, call.children[0].name))
162def batch_recipe_example():
163 print("example batch analysis using recipe:\n")
164 recipe_ast_processor = RecipeASTProcessor(AnalysisRecipe(), simple_codebase_provider, r".*", in_memory=True)
165 recipe_ast_processor.run()
168if __name__ == "__main__":
169 # a list of example to show batch processing of a code base
170 batch_remove_unused_variable_once_example()
171 batch_repeat_example()
172 batch_recipe_example()