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

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

73 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 copy import deepcopy 

11from typing import Any, cast 

12 

13from . import c_ast 

14 

15 

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

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

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

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

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

21 

22 switch (myvar) { 

23 case 10: 

24 k = 10; 

25 p = k + 1; 

26 return 10; 

27 case 20: 

28 case 30: 

29 return 20; 

30 default: 

31 break; 

32 } 

33 

34 Creates this tree (pseudo-dump): 

35 

36 Switch 

37 ID: myvar 

38 Compound: 

39 Case 10: 

40 k = 10 

41 p = k + 1 

42 return 10 

43 Case 20: 

44 Case 30: 

45 return 20 

46 Default: 

47 break 

48 

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

50 following: 

51 

52 Switch 

53 ID: myvar 

54 Compound: 

55 Case 10: 

56 k = 10 

57 p = k + 1 

58 return 10 

59 Case 20: 

60 Case 30: 

61 return 20 

62 Default: 

63 break 

64 

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

66 """ 

67 assert isinstance(switch_node, c_ast.Switch) 

68 if isinstance(switch_node.stmt, c_ast.AttributedStatement): 

69 attributed = switch_node.stmt 

70 switch_node.stmt = attributed.stmt 

71 fix_switch_cases(switch_node) 

72 attributed.stmt = switch_node.stmt 

73 switch_node.stmt = attributed 

74 return switch_node 

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

76 return switch_node 

77 

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

79 # correct order 

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

81 

82 # The last Case/Default node 

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

84 

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

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

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

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

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

90 # If it's a Case/Default: 

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

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

93 # to a sibling. 

94 new_compound.block_items.append(child) 

95 _extract_nested_case(child, new_compound.block_items) 

96 last_case = new_compound.block_items[-1] 

97 else: 

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

99 # exists. 

100 if last_case is None: 

101 new_compound.block_items.append(child) 

102 else: 

103 last_case.stmts.append(child) 

104 

105 switch_node.stmt = new_compound 

106 return switch_node 

107 

108 

109def _extract_nested_case( 

110 case_node: c_ast.Case | c_ast.Default, stmts_list: list[c_ast.Node] 

111) -> None: 

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

113 by the parser and add them to the stmts_list. 

114 """ 

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

116 nested = case_node.stmts.pop() 

117 stmts_list.append(nested) 

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

119 

120 

121def fix_atomic_specifiers( 

122 decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename, 

123) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename: 

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

125 conferring a qualifier upon the contained type. 

126 

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

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

129 the _Atomic qualifier in the right place. 

130 """ 

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

132 # fixed point is reached. 

133 while True: 

134 decl, found = _fix_atomic_specifiers_once(decl) 

135 if not found: 

136 break 

137 

138 # Match the declaration's qualifiers to the innermost TypeDecl, as for 

139 # ordinary declarations. Qualifiers moved onto pointers belong only on 

140 # those PtrDecl nodes. Also restore the name lost with the wrapper TypeDecl. 

141 typ: Any = decl 

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

143 try: 

144 typ = typ.type 

145 except AttributeError: 

146 return decl 

147 decl.quals = typ.quals[:] 

148 if typ.declname is None: 

149 typ.declname = decl.name 

150 

151 return decl 

152 

153 

154def _fix_atomic_specifiers_once( 

155 decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename, 

156) -> tuple[c_ast.Decl | c_ast.Typedef | c_ast.Typename, bool]: 

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

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

159 """ 

160 parent: Any = decl 

161 grandparent: Any = None 

162 node: Any = decl.type 

163 while node is not None: 

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

165 break 

166 try: 

167 grandparent = parent 

168 parent = node 

169 node = node.type 

170 except AttributeError: 

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

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

173 # and return the original decl unmodified. 

174 return decl, False 

175 

176 assert isinstance(parent, c_ast.TypeDecl) 

177 assert grandparent is not None 

178 # _Atomic(T) cannot wrap an array or function type (C11 6.7.2.4p3). Such a T 

179 # has no `quals` to merge below, so report it instead of letting the attach 

180 # raise AttributeError out of parse(). 

181 if isinstance(node.type, (c_ast.ArrayDecl, c_ast.FuncDecl)): 

182 # Imported here to avoid a circular import at module load time. 

183 from .c_parser import ParseError 

184 

185 raise ParseError( 

186 f"{node.coord}: _Atomic cannot be applied to array or function type" 

187 ) 

188 # Declaration specifiers are shared by comma-separated declarators. Copy 

189 # the contained type before attaching qualifiers, coordinates, and a name. 

190 typ = deepcopy(node.type) 

191 if typ.coord is None: 

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

193 # its location when we replace the wrapper Typename. 

194 typ.coord = parent.coord 

195 # Qualifiers outside _Atomic(T) apply to T itself. In particular, const in 

196 # const _Atomic(int *) qualifies the pointer, not the pointed-to int. 

197 typ.quals = parent.quals + typ.quals 

198 if "_Atomic" not in typ.quals: 

199 typ.quals.append("_Atomic") 

200 cast(Any, grandparent).type = typ 

201 return decl, True