Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pycparser/ast_transforms.py: 46%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

61 statements  

1# ------------------------------------------------------------------------------ 

2# pycparser: ast_transforms.py 

3# 

4# Some utilities used by the parser to create a friendlier AST. 

5# 

6# Eli Bendersky [https://eli.thegreenplace.net/] 

7# License: BSD 

8# ------------------------------------------------------------------------------ 

9 

10from typing import Any, List, Tuple, cast 

11 

12from . import c_ast 

13 

14 

15def fix_switch_cases(switch_node: c_ast.Switch) -> c_ast.Switch: 

16 """The 'case' statements in a 'switch' come out of parsing with one 

17 child node, so subsequent statements are just tucked to the parent 

18 Compound. Additionally, consecutive (fall-through) case statements 

19 come out messy. This is a peculiarity of the C grammar. The following: 

20 

21 switch (myvar) { 

22 case 10: 

23 k = 10; 

24 p = k + 1; 

25 return 10; 

26 case 20: 

27 case 30: 

28 return 20; 

29 default: 

30 break; 

31 } 

32 

33 Creates this tree (pseudo-dump): 

34 

35 Switch 

36 ID: myvar 

37 Compound: 

38 Case 10: 

39 k = 10 

40 p = k + 1 

41 return 10 

42 Case 20: 

43 Case 30: 

44 return 20 

45 Default: 

46 break 

47 

48 The goal of this transform is to fix this mess, turning it into the 

49 following: 

50 

51 Switch 

52 ID: myvar 

53 Compound: 

54 Case 10: 

55 k = 10 

56 p = k + 1 

57 return 10 

58 Case 20: 

59 Case 30: 

60 return 20 

61 Default: 

62 break 

63 

64 A fixed AST node is returned. The argument may be modified. 

65 """ 

66 assert isinstance(switch_node, c_ast.Switch) 

67 if not isinstance(switch_node.stmt, c_ast.Compound): 

68 return switch_node 

69 

70 # The new Compound child for the Switch, which will collect children in the 

71 # correct order 

72 new_compound = c_ast.Compound([], switch_node.stmt.coord) 

73 

74 # The last Case/Default node 

75 last_case: c_ast.Case | c_ast.Default | None = None 

76 

77 # Goes over the children of the Compound below the Switch, adding them 

78 # either directly below new_compound or below the last Case as appropriate 

79 # (for `switch(cond) {}`, block_items would have been None) 

80 for child in switch_node.stmt.block_items or []: 

81 if isinstance(child, (c_ast.Case, c_ast.Default)): 

82 # If it's a Case/Default: 

83 # 1. Add it to the Compound and mark as "last case" 

84 # 2. If its immediate child is also a Case or Default, promote it 

85 # to a sibling. 

86 new_compound.block_items.append(child) 

87 _extract_nested_case(child, new_compound.block_items) 

88 last_case = new_compound.block_items[-1] 

89 else: 

90 # Other statements are added as children to the last case, if it 

91 # exists. 

92 if last_case is None: 

93 new_compound.block_items.append(child) 

94 else: 

95 last_case.stmts.append(child) 

96 

97 switch_node.stmt = new_compound 

98 return switch_node 

99 

100 

101def _extract_nested_case( 

102 case_node: c_ast.Case | c_ast.Default, stmts_list: List[c_ast.Node] 

103) -> None: 

104 """Recursively extract consecutive Case statements that are made nested 

105 by the parser and add them to the stmts_list. 

106 """ 

107 if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)): 

108 nested = case_node.stmts.pop() 

109 stmts_list.append(nested) 

110 _extract_nested_case(cast(Any, nested), stmts_list) 

111 

112 

113def fix_atomic_specifiers( 

114 decl: c_ast.Decl | c_ast.Typedef, 

115) -> c_ast.Decl | c_ast.Typedef: 

116 """Atomic specifiers like _Atomic(type) are unusually structured, 

117 conferring a qualifier upon the contained type. 

118 

119 This function fixes a decl with atomic specifiers to have a sane AST 

120 structure, by removing spurious Typename->TypeDecl pairs and attaching 

121 the _Atomic qualifier in the right place. 

122 """ 

123 # There can be multiple levels of _Atomic in a decl; fix them until a 

124 # fixed point is reached. 

125 while True: 

126 decl, found = _fix_atomic_specifiers_once(decl) 

127 if not found: 

128 break 

129 

130 # Make sure to add an _Atomic qual on the topmost decl if needed. Also 

131 # restore the declname on the innermost TypeDecl (it gets placed in the 

132 # wrong place during construction). 

133 typ: Any = decl 

134 while not isinstance(typ, c_ast.TypeDecl): 

135 try: 

136 typ = typ.type 

137 except AttributeError: 

138 return decl 

139 if "_Atomic" in typ.quals and "_Atomic" not in decl.quals: 

140 decl.quals.append("_Atomic") 

141 if typ.declname is None: 

142 typ.declname = decl.name 

143 

144 return decl 

145 

146 

147def _fix_atomic_specifiers_once( 

148 decl: c_ast.Decl | c_ast.Typedef, 

149) -> Tuple[c_ast.Decl | c_ast.Typedef, bool]: 

150 """Performs one 'fix' round of atomic specifiers. 

151 Returns (modified_decl, found) where found is True iff a fix was made. 

152 """ 

153 parent: Any = decl 

154 grandparent: Any = None 

155 node: Any = decl.type 

156 while node is not None: 

157 if isinstance(node, c_ast.Typename) and "_Atomic" in node.quals: 

158 break 

159 try: 

160 grandparent = parent 

161 parent = node 

162 node = node.type 

163 except AttributeError: 

164 # If we've reached a node without a `type` field, it means we won't 

165 # find what we're looking for at this point; give up the search 

166 # and return the original decl unmodified. 

167 return decl, False 

168 

169 assert isinstance(parent, c_ast.TypeDecl) 

170 assert grandparent is not None 

171 if node.type.coord is None: 

172 # Preserve the declarator coord for _Atomic(T) so TypeDecl doesn't lose 

173 # its location when we replace the wrapper Typename. 

174 node.type.coord = parent.coord 

175 cast(Any, grandparent).type = node.type 

176 if "_Atomic" not in node.type.quals: 

177 node.type.quals.append("_Atomic") 

178 return decl, True