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

71 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-09-09 14:04 +0000

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

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

3from renaissance.integrations.clang import ClangASTNode, CPatternFactory 

4from renaissance.integrations.types import TypeReference 

5from renaissance.syntax_tree import ( 

6 ASTFactory, 

7 ASTFinder, 

8 ASTRewriter, 

9 ASTShower, 

10) 

11from renaissance.syntax_tree.ast_finder import find_ast_type, matches_kind 

12from renaissance.syntax_tree.match_finder import find_all, match_pattern 

13 

14example_code = """ 

15 typedef int fancy_new; 

16 typedef int old; 

17 void f(){ 

18 int a = 1; 

19 old b = 2; 

20 int c = 3; 

21 old d = 4; 

22 old e; 

23 } 

24 """ 

25expected_result_old_fancy_new = """ 

26 typedef int fancy_new; 

27 typedef int old; 

28 void f(){ 

29 int a = 1; 

30 fancy_new b = 2; 

31 int c = 3; 

32 fancy_new d = 4; 

33 fancy_new e; 

34 } 

35 """.strip() 

36 

37expected_result_old_with_comment = """ 

38 typedef int fancy_new; 

39 typedef int old; 

40 void f(){ 

41 int a = 1; 

42 // old has become obsolete 

43 old b = 2; 

44 int c = 3; 

45 // old has become obsolete 

46 old d = 4; 

47 // old has become obsolete 

48 old e; 

49 } 

50 """.strip() 

51 

52 

53def example_add_comment_and_commit(factory, pattern_factory): 

54 # create a pattern that matches the declaration of old 

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

56 pattern1 = pattern_factory.create_declarations( 

57 "old $name = $value;", 

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

59 parameters=["$value"], 

60 ) 

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

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

63 patterns_list = [pattern1, pattern2] 

64 

65 ASTShower.show_node(pattern1[0]) 

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

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

68 

69 # create translation unit 

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

71 

72 ASTShower.show_node(atu) 

73 

74 # create an ASTRewriter 

75 rewriter = ASTRewriter(atu) 

76 

77 # search matches and replace them 

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

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

80 

81 def commit(): 

82 rewriter.apply_to_string() 

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

84 return atu, ASTRewriter(atu) 

85 

86 # commit 

87 atu, rewriter = commit() 

88 

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

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

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

92 print(result) 

93 return result, expected_result_old_with_comment 

94 

95 

96def example_replace_old_by_fancy_new(factory, pattern_factory): 

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

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

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

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

101 patterns_list = [pattern1, pattern2] 

102 

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

104 def matches_old(node): 

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

106 

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

108 rewriter = ASTRewriter(atu) 

109 

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

111 

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

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

114 print(result) 

115 return result, expected_result_old_fancy_new 

116 

117 

118def example_use_ast_kind_finder(factory, _): 

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

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

121 # Create an ASTRewriter for the translation unit 

122 rewriter = ASTRewriter(atu) 

123 

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

125 [rewriter.replace("fancy_new", node) for node in find_ast_type(atu, TypeReference) if node.name == "old"] 

126 

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

128 print("results after replacing the old type by fancy_new using find_ast_type") 

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

130 print(result) 

131 return result, expected_result_old_fancy_new 

132 

133 

134def example_use_ast_function_finder(factory, _): 

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

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

137 # Create an ASTRewriter for the translation unit 

138 rewriter = ASTRewriter(atu) 

139 

140 ASTShower.show_node(atu) 

141 

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

143 def match(node): 

144 res = matches_kind(node, TypeReference) and node.name == "old" 

145 return res 

146 

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

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

149 

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

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

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

153 print(result) 

154 return result, expected_result_old_fancy_new 

155 

156 

157def main(args): 

158 # the first argument is the code to be parsed 

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

160 

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

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

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

164 pattern_factory = CPatternFactory(factory) 

165 

166 example_add_comment_and_commit(factory, pattern_factory) 

167 example_replace_old_by_fancy_new(factory, pattern_factory) 

168 example_use_ast_kind_finder(factory, pattern_factory) 

169 example_use_ast_function_finder(factory, pattern_factory) 

170 

171 

172if __name__ == "__main__": 

173 import sys 

174 

175 main(sys.argv)