Coverage for src/rejuvenation/refactor_examples_different_styles.py: 97%

71 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-10-02 13:12 +0000

1"""AI: Example script demonstrating multiple C AST refactoring styles: adding comments, replacing types, and finding nodes.""" 

2 

3# This script demonstrates various techniques for refactoring C code using an abstract syntax tree (AST) approach. 

4# It showcases how to add comments, replace types, and find specific nodes in the AST using different methods. 

5from renaissance.integrations.clang import ClangASTNode, CPatternFactory 

6from renaissance.integrations.clang.predicates import is_clang_type_reference 

7from renaissance.syntax_tree import ( 

8 ASTFactory, 

9 ASTFinder, 

10 ASTRewriter, 

11 ASTShower, 

12) 

13from renaissance.syntax_tree.ast_finder import find_nodes 

14from renaissance.syntax_tree.match_finder import find_all, match_pattern 

15 

16example_code = """ 

17 typedef int fancy_new; 

18 typedef int old; 

19 void f(){ 

20 int a = 1; 

21 old b = 2; 

22 int c = 3; 

23 old d = 4; 

24 old e; 

25 } 

26 """ 

27expected_result_old_fancy_new = """ 

28 typedef int fancy_new; 

29 typedef int old; 

30 void f(){ 

31 int a = 1; 

32 fancy_new b = 2; 

33 int c = 3; 

34 fancy_new d = 4; 

35 fancy_new e; 

36 } 

37 """.strip() 

38 

39expected_result_old_with_comment = """ 

40 typedef int fancy_new; 

41 typedef int old; 

42 void f(){ 

43 int a = 1; 

44 // old has become obsolete 

45 old b = 2; 

46 int c = 3; 

47 // old has become obsolete 

48 old d = 4; 

49 // old has become obsolete 

50 old e; 

51 } 

52 """.strip() 

53 

54 

55def example_add_comment_and_commit(factory, pattern_factory): 

56 """AI: Insert a comment before each obsolete 'old' declaration, commit, and return the rewritten source.""" 

57 # create a pattern that matches the declaration of old 

58 # please note that we need to help by telling the old is a type and $value is a variable 

59 pattern1 = pattern_factory.create_declarations( 

60 "old $name = $value;", 

61 extra_declarations=["typedef int old;"], 

62 parameters=["$value"], 

63 ) 

64 pattern2 = pattern_factory.create_declarations("old $name;", extra_declarations=["typedef int old;"], parameters=["$value"]) 

65 # put the patterns in a matrix because we want to find both statements in one go and not a sequence 

66 patterns_list = [pattern1, pattern2] 

67 

68 ASTShower.show_node(pattern1[0]) 

69 # if you want to find both statements in one go, you should pass a list of patterns 

70 # if you don't do that a sequence of the patterns is searched for 

71 

72 # create translation unit 

73 atu = factory.create_from_text(example_code, "test.c") 

74 

75 ASTShower.show_node(atu) 

76 

77 # create an ASTRewriter 

78 rewriter = ASTRewriter(atu) 

79 

80 # search matches and replace them 

81 for match in find_all(atu.children, *patterns_list): 

82 rewriter.insert_before("// old has become obsolete", match) 

83 

84 def commit(): 

85 rewriter.apply_to_string() 

86 atu = factory.create_from_text(rewriter.apply_to_string(), rewriter.get_filename()) 

87 return atu, ASTRewriter(atu) 

88 

89 # commit 

90 atu, rewriter = commit() 

91 

92 # look at the print that marks all old declarations with the provided comment 

93 print("results after adding comments to the obsolete types:") 

94 result = rewriter.apply_to_string().strip() 

95 print(result) 

96 return result, expected_result_old_with_comment 

97 

98 

99def example_replace_old_by_fancy_new(factory, pattern_factory): 

100 """AI: Replace 'old' declarations with 'fancy_new' using pattern matching and filtering.""" 

101 # using some different techniques to show the possibilities of map and filter 

102 pattern1 = pattern_factory.create_declarations("$old $name = $value;", types=["$old"], parameters=["$value"]) 

103 pattern2 = pattern_factory.create_declarations("$old $name;", types=["$old"], parameters=["$value"]) 

104 # put the patterns in a matrix because we want to find both statements in one go and not a sequence 

105 patterns_list = [pattern1, pattern2] 

106 

107 # an example of how to use a function iso of lambda to filter the nodes 

108 def matches_old(node): 

109 return bool("$old" in node and node["$old"][0].name == "old") 

110 

111 atu = factory.create_from_text(example_code, "test.c") 

112 rewriter = ASTRewriter(atu) 

113 

114 [rewriter.replace("fancy_new", match.nodes) for match in match_pattern(atu.children, *patterns_list) if matches_old(match.expansions)] 

115 

116 print("results after replacing the old type by fancy_new using MatchFinder:") 

117 result = rewriter.apply_to_string().strip() 

118 print(result) 

119 return result, expected_result_old_fancy_new 

120 

121 

122def example_use_ast_kind_finder(factory, _): 

123 """AI: Replace 'old' type references with 'fancy_new' using a Clang node-kind predicate.""" 

124 # Create the translation unit from the provided code or example code 

125 atu = factory.create_from_text(example_code, "test.c") 

126 # Create an ASTRewriter for the translation unit 

127 rewriter = ASTRewriter(atu) 

128 

129 # Find all nodes of kind TYPE_REF (case-insensitive) and filter those with name 'old' 

130 [rewriter.replace("fancy_new", node) for node in find_nodes(atu, is_clang_type_reference) if node.name == "old"] 

131 

132 # Print the results after replacing the old type by fancy_new 

133 print("results after replacing the old type by fancy_new using a Clang predicate") 

134 result = rewriter.apply_to_string().strip() 

135 print(result) 

136 return result, expected_result_old_fancy_new 

137 

138 

139def example_use_ast_function_finder(factory, _): 

140 """AI: Replace 'old' type references with 'fancy_new' using `ASTFinder.find_all`.""" 

141 # Create the translation unit from the provided code or example code 

142 atu = factory.create_from_text(example_code, "test.c") 

143 # Create an ASTRewriter for the translation unit 

144 rewriter = ASTRewriter(atu) 

145 

146 ASTShower.show_node(atu) 

147 

148 # Define a match function to find nodes of kind TYPE_REF with name 'old' 

149 def match(node): 

150 res = is_clang_type_reference(node) and node.name == "old" 

151 return res 

152 

153 # Use ASTFinder to find all matching nodes and replace 'old' with 'fancy_new' 

154 [rewriter.replace("fancy_new", node) for node in ASTFinder.find_all(atu, match)] 

155 

156 # Print the results after replacing the old type by fancy_new 

157 print("results after replacing the old type by fancy_new using ASTFinder.find_all") 

158 result = rewriter.apply_to_string().strip() 

159 print(result) 

160 return result, expected_result_old_fancy_new 

161 

162 

163def main(args): 

164 """AI: Run all four 'old'-to-'fancy_new' refactoring style examples against the parsed args.""" 

165 # the first argument is the code to be parsed 

166 code = args[1] if len(args) > 1 else "" 

167 

168 # Create a factory args from the command line are passed to the factory for example -I/usr/include 

169 factory = ASTFactory(ClangASTNode, args if not code else args[1:]) 

170 # Create a pattern factory (using the factory (hence also its args) 

171 pattern_factory = CPatternFactory(factory) 

172 

173 example_add_comment_and_commit(factory, pattern_factory) 

174 example_replace_old_by_fancy_new(factory, pattern_factory) 

175 example_use_ast_kind_finder(factory, pattern_factory) 

176 example_use_ast_function_finder(factory, pattern_factory) 

177 

178 

179if __name__ == "__main__": 

180 import sys 

181 

182 main(sys.argv)