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

71 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-15 10:34 +0000

1# Copyright (C) 2024-2026 TNO 

2# 

3# Licensed under the terms specified in the LICENSE file in the root directory 

4# of this repository, also available at: 

5# https://github.com/TNO/Renaissance.Py/blob/main/LICENSE 

6 

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

8 

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

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

11from renaissance.integrations.clang import ClangASTNode, CPatternFactory 

12from renaissance.integrations.clang.predicates import is_clang_type_reference 

13from renaissance.syntax_tree import ( 

14 ASTFactory, 

15 ASTFinder, 

16 ASTRewriter, 

17 ASTShower, 

18) 

19from renaissance.syntax_tree.ast_finder import find_nodes 

20from renaissance.syntax_tree.match_finder import find_all, match_pattern 

21 

22example_code = """ 

23 typedef int fancy_new; 

24 typedef int old; 

25 void f(){ 

26 int a = 1; 

27 old b = 2; 

28 int c = 3; 

29 old d = 4; 

30 old e; 

31 } 

32 """ 

33expected_result_old_fancy_new = """ 

34 typedef int fancy_new; 

35 typedef int old; 

36 void f(){ 

37 int a = 1; 

38 fancy_new b = 2; 

39 int c = 3; 

40 fancy_new d = 4; 

41 fancy_new e; 

42 } 

43 """.strip() 

44 

45expected_result_old_with_comment = """ 

46 typedef int fancy_new; 

47 typedef int old; 

48 void f(){ 

49 int a = 1; 

50 // old has become obsolete 

51 old b = 2; 

52 int c = 3; 

53 // old has become obsolete 

54 old d = 4; 

55 // old has become obsolete 

56 old e; 

57 } 

58 """.strip() 

59 

60 

61def example_add_comment_and_commit(factory, pattern_factory): 

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

63 # create a pattern that matches the declaration of old 

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

65 pattern1 = pattern_factory.create_declarations( 

66 "old $name = $value;", 

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

68 parameters=["$value"], 

69 ) 

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

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

72 patterns_list = [pattern1, pattern2] 

73 

74 ASTShower.show_node(pattern1[0]) 

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

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

77 

78 # create translation unit 

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

80 

81 ASTShower.show_node(atu) 

82 

83 # create an ASTRewriter 

84 rewriter = ASTRewriter(atu) 

85 

86 # search matches and replace them 

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

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

89 

90 def commit(): 

91 rewriter.apply_to_string() 

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

93 return atu, ASTRewriter(atu) 

94 

95 # commit 

96 atu, rewriter = commit() 

97 

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

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

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

101 print(result) 

102 return result, expected_result_old_with_comment 

103 

104 

105def example_replace_old_by_fancy_new(factory, pattern_factory): 

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

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

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

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

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

111 patterns_list = [pattern1, pattern2] 

112 

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

114 def matches_old(node): 

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

116 

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

118 rewriter = ASTRewriter(atu) 

119 

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

121 

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

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

124 print(result) 

125 return result, expected_result_old_fancy_new 

126 

127 

128def example_use_ast_kind_finder(factory, _): 

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

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

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

132 # Create an ASTRewriter for the translation unit 

133 rewriter = ASTRewriter(atu) 

134 

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

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

137 

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

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

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

141 print(result) 

142 return result, expected_result_old_fancy_new 

143 

144 

145def example_use_ast_function_finder(factory, _): 

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

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

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

149 # Create an ASTRewriter for the translation unit 

150 rewriter = ASTRewriter(atu) 

151 

152 ASTShower.show_node(atu) 

153 

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

155 def match(node): 

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

157 return res 

158 

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

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

161 

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

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

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

165 print(result) 

166 return result, expected_result_old_fancy_new 

167 

168 

169def main(args) -> None: 

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

171 # the first argument is the code to be parsed 

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

173 

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

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

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

177 pattern_factory = CPatternFactory(factory) 

178 

179 example_add_comment_and_commit(factory, pattern_factory) 

180 example_replace_old_by_fancy_new(factory, pattern_factory) 

181 example_use_ast_kind_finder(factory, pattern_factory) 

182 example_use_ast_function_finder(factory, pattern_factory) 

183 

184 

185if __name__ == "__main__": 

186 import sys 

187 

188 main(sys.argv)