Coverage for src / rejuvenation / refactor_with_nested_compositions.py: 94%

50 statements  

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

1# This script demonstrates the use of the syntax_tree library to parse and rewrite C code. 

2# It specifically showcases nested replacements and multiple patterns. 

3import textwrap 

4 

5from renaissance.integrations.clang import ClangASTNode, CPatternFactory 

6from renaissance.integrations.types import Call 

7from renaissance.syntax_tree import ASTFactory, ASTRewriter, ASTShower 

8from renaissance.syntax_tree.ast_finder import find_ast_type 

9from renaissance.syntax_tree.match_finder import find_all 

10 

11example_code = """ 

12void f1(int a, int b, int c); 

13void f2(int a, int c); 

14void f(){ 

15 const int a = 1; 

16 const int b = 2; 

17 int isAOne = a==1; 

18 int c = 0, d=0; 

19 if (a==1) { 

20 d++; 

21 if(a==1){ 

22 d++; 

23 c=d; 

24 f1(a,b,c); 

25 } 

26 } 

27 if (a==2) { 

28 c++; 

29 f1(a,b,c); 

30 } 

31 f1(a,b,c); 

32} 

33""".strip() 

34 

35expected_result = """ 

36void f1(int a, int b, int c); 

37void f2(int a, int c); 

38void f(){ 

39 const int a = 1; 

40 const int b = 2; 

41 int isAOne = a==1; 

42 int c = 0, d=0; 

43 //changed if expr to const 

44 if(isAOne){ 

45 d++; 

46 //changed if expr to const 

47 if(isAOne){ 

48 d++; 

49 c=d; 

50 //changed function f1 to f2 

51 f2(a,c); 

52 } 

53 } 

54 if (a==2) { 

55 c++; 

56 //changed function f1 to f2 

57 f2(a,c); 

58 } 

59 //changed function f1 to f2 

60 f2(a,c); 

61} 

62""".strip() 

63 

64 

65def refactor_with_nested_compositions(args): 

66 # the first argument is the code to be parsed 

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

68 

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

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

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

72 # create translation unit 

73 atu = factory.create(code) if code else factory.create_from_text(example_code, "example.c") 

74 # create a pattern factory atu is passed to the pattern factory for use of all # includes, #defines and declarations 

75 pattern_factory = CPatternFactory(factory, atu) 

76 # create a pattern that matches an if statement with a==1 as the condition and a block of statements as the body 

77 # the type is important so it's declared as const int a 

78 pattern1 = pattern_factory.create_statements("if(a==1){$$stmts;}", extra_declarations=["const int a;"]) 

79 # for pattern 2 we create a fully functional c snippet with a call to f1 

80 # note that the f1 declaration is derived from the atu 

81 pattern2 = pattern_factory.create("int $a,$b,$c; void fff() {f1($a,$b,$c);}") 

82 ASTShower.show_node(pattern1[0], include_properties=True) 

83 

84 # we only want to search the call expression as a pattern so it's searched using the kind 

85 pattern2 = find_ast_type(pattern2, Call) 

86 

87 # the replacement code strip indent is used to be agnostic to the indentation of the replacement 

88 pattern1replacement = textwrap.dedent(""" 

89 //changed if expr to const 

90 if(isAOne){ 

91 $$stmts; 

92 }""") 

93 

94 pattern2replacement = "\n//changed function f1 to f2\nf2($a,$c);" 

95 

96 # show node and patterns enable include properties to show the properties of the nodes 

97 include_properties = True 

98 ASTShower.show_node(atu, include_properties) 

99 ASTShower.show_node(pattern1[0], include_properties) 

100 ASTShower.show_node(pattern2[0], include_properties) 

101 

102 result1 = None 

103 while atu: 

104 # create an ASTRewriter 

105 rewriter = ASTRewriter(atu) 

106 

107 def raw(nodes): 

108 res = "" 

109 for node in nodes: 

110 res += node.text 

111 return res + "\n" 

112 

113 # create a refactoring that use different replacement code for different patterns 

114 def refactor(match1): 

115 print(f"peek: f{match1.signature}") 

116 if match1.patterns == pattern1: 

117 replacement_text = pattern1replacement 

118 for repl_snippet in match1.expansions: 

119 replacement_text = replacement_text.replace(repl_snippet, raw(match1.expansions[repl_snippet])) 

120 else: 

121 replacement_text = pattern2replacement 

122 for repl_snippet in match1.expansions: 

123 replacement_text = replacement_text.replace(repl_snippet, match1.expansions[repl_snippet][0].signature) 

124 return rewriter.replace(replacement_text, match1.nodes) 

125 

126 # search matches for pattern1 and pattern2 and replace them using the refactor function 

127 for match in find_all(atu.children, pattern1, pattern2): 

128 refactor(match) 

129 

130 # print the rewritten code 

131 result1 = rewriter.apply_to_string() 

132 atu = factory.create_from_text(result1, "example.c") if rewriter.has_changed() else None 

133 return result1 

134 

135 

136if __name__ == "__main__": 

137 import sys 

138 

139 result = refactor_with_nested_compositions(sys.argv) 

140 print(result)