-
Notifications
You must be signed in to change notification settings - Fork 1
/
macros.py
323 lines (274 loc) · 10.4 KB
/
macros.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
from dataclasses import dataclass, field
from typing import Literal, Set
from translationconfig import IntSize
@dataclass(frozen=True)
class Macro:
Name: str
IsObjectLike: bool
IsDefinitionLocationValid: bool
IsDefinedAtGlobalScope: bool
Body: str
DefinitionLocation: str
EndDefinitionLocation: str
def defined_in(self, dir: str):
'''Returns true if the macro was defined in the given dir'''
return self.IsDefinitionLocationValid and self.DefinitionLocation.startswith(dir)
@property
def IsFunctionLike(self) -> bool:
return not self.IsObjectLike
@dataclass(frozen=True)
class Invocation:
Name: str
DefinitionLocation: str
InvocationLocation: str
ASTKind: Literal['Decl', 'Stmt', 'TypeLoc', 'Expr']
TypeSignature: str
InvocationDepth: int
NumASTRoots: int
NumArguments: int
HasStringification: bool
HasTokenPasting: bool
HasAlignedArguments: bool
HasSameNameAsOtherDeclaration: bool
IsExpansionControlFlowStmt: bool
DoesBodyReferenceMacroDefinedAfterMacro: bool
DoesBodyReferenceDeclDeclaredAfterMacro: bool
DoesBodyContainDeclRefExpr: bool
DoesBodyEndWithCompoundStmt: bool
DoesSubexpressionExpandedFromBodyHaveLocalType: bool
DoesSubexpressionExpandedFromBodyHaveTypeDefinedAfterMacro: bool
DoesAnyArgumentHaveSideEffects: bool
DoesAnyArgumentContainDeclRefExpr: bool
IsHygienic: bool
IsICERepresentableByInt32: bool
IsICERepresentableByInt16: bool
IsDefinitionLocationValid: bool
IsInvocationLocationValid: bool
IsObjectLike: bool
IsInvokedInMacroArgument: bool
IsNamePresentInCPPConditional: bool
IsExpansionICE: bool
IsExpansionTypeNull: bool
IsExpansionTypeAnonymous: bool
IsExpansionTypeLocalType: bool
IsExpansionTypeDefinedAfterMacro: bool
IsExpansionTypeVoid: bool
IsExpansionTypeFunctionType: bool
IsAnyArgumentTypeNull: bool
IsAnyArgumentTypeAnonymous: bool
IsAnyArgumentTypeLocalType: bool
IsAnyArgumentTypeDefinedAfterMacro: bool
IsAnyArgumentTypeVoid: bool
IsAnyArgumentTypeFunctionType: bool
IsInvokedWhereModifiableValueRequired: bool
IsInvokedWhereAddressableValueRequired: bool
IsInvokedInSizeOf: bool
IsInvokedWhereICERequired: bool
IsInvokedWhereConstantExpressionRequired: bool
IsAnyArgumentExpandedWhereModifiableValueRequired: bool
IsAnyArgumentExpandedWhereAddressableValueRequired: bool
IsAnyArgumentExpandedInSizeOf: bool
IsAnyArgumentConditionallyEvaluated: bool
IsAnyArgumentExpandedWhereConstExprRequired: bool
IsAnyArgumentNeverExpanded: bool
IsAnyArgumentNotAnExpression: bool
@property
def DefinitionLocationFilename(self) -> str:
if not self.IsDefinitionLocationValid:
return self.DefinitionLocation
else:
file, _line, _col = self.DefinitionLocation.split(':')
return file
@property
def IsFunctionLike(self) -> bool:
return not self.IsObjectLike
@property
def IsTopLevelNonArgument(self) -> bool:
return all([self.InvocationDepth == 0,
not self.IsInvokedInMacroArgument,
self.IsInvocationLocationValid,
self.IsDefinitionLocationValid])
@property
def IsAligned(self) -> bool:
assert self.IsTopLevelNonArgument
return all([self.IsTopLevelNonArgument,
self.NumASTRoots == 1,
self.HasAlignedArguments])
@property
def HasSemanticData(self) -> bool:
return all([
# TODO: Check that we don't end with a compound statement
self.IsTopLevelNonArgument,
not self.IsAnyArgumentNeverExpanded,
self.IsAligned,
not (self.ASTKind == 'Expr' and self.IsExpansionTypeNull)
])
@property
def CanBeTurnedIntoEnum(self) -> bool:
assert self.HasSemanticData
# Enums have to be ICEs
return self.IsExpansionICE
@property
def CanBeTurnedIntoVariable(self) -> bool:
assert self.HasSemanticData
return all([
# Variables must be exprs
self.ASTKind == 'Expr',
# Variables cannot contain DeclRefExprs
not self.DoesBodyContainDeclRefExpr,
# Variables cannot be invoked where ICEs are required
not self.IsInvokedWhereICERequired,
# Variables cannot have the void type
not self.IsExpansionTypeVoid
])
@property
def IsExpansionConstantExpression(self) -> bool:
return all([
self.ASTKind == 'Expr',
# Variables cannot contain DeclRefExprs
not self.DoesBodyContainDeclRefExpr,
])
@property
def CanBeTurnedIntoEnumOrVariable(self) -> bool:
assert self.HasSemanticData
return self.CanBeTurnedIntoEnum or self.CanBeTurnedIntoVariable
@property
def CanBeTurnedIntoFunction(self) -> bool:
assert self.HasSemanticData
return all([
# Functions must be stmts or expressions
(self.ASTKind == 'Stmt' or self.ASTKind == 'Expr'),
# Functions cannot be invoked where ICEs are required
not self.IsInvokedWhereICERequired
])
@property
def CanBeTurnedIntoAFunctionOrVariable(self) -> bool:
assert self.HasSemanticData
return (self.CanBeTurnedIntoFunction or
self.CanBeTurnedIntoVariable)
@property
def CanBeTurnedIntoTypeDef(self) -> bool:
assert self.HasSemanticData
return self.ASTKind == 'TypeLoc'
@property
def MustAlterArgumentsOrReturnTypeToTransform(self) -> bool:
assert self.HasSemanticData
return any([
not self.IsHygienic,
self.IsInvokedWhereModifiableValueRequired,
self.IsInvokedWhereAddressableValueRequired,
self.IsAnyArgumentExpandedWhereModifiableValueRequired,
self.IsAnyArgumentExpandedWhereAddressableValueRequired
])
@property
def MustAlterDeclarationsToTransform(self) -> bool:
assert self.HasSemanticData
return any([
self.HasSameNameAsOtherDeclaration,
self.DoesBodyReferenceMacroDefinedAfterMacro,
self.DoesBodyReferenceDeclDeclaredAfterMacro,
self.DoesSubexpressionExpandedFromBodyHaveLocalType,
self.DoesSubexpressionExpandedFromBodyHaveTypeDefinedAfterMacro,
self.IsExpansionTypeAnonymous,
self.IsExpansionTypeLocalType,
self.IsExpansionTypeDefinedAfterMacro,
self.ASTKind == 'TypeLoc'
])
@property
def MustAlterCallSiteToTransform(self) -> bool:
if not self.IsAligned:
return True
assert self.HasSemanticData
return self.IsAnyArgumentConditionallyEvaluated
@property
def MustCreateThunksToTransform(self) -> bool:
return any([
self.DoesAnyArgumentHaveSideEffects,
self.IsAnyArgumentTypeVoid
])
@property
def MustUseMetaprogrammingToTransform(self) -> bool:
return ((self.HasStringification or self.HasTokenPasting) or
(self.HasSemanticData and
self.IsFunctionLike and
self.CanBeTurnedIntoFunction and
self.IsAnyArgumentNotAnExpression) or
self.IsExpansionControlFlowStmt or
self.IsNamePresentInCPPConditional)
@property
def SatisfiesASyntacticProperty(self) -> bool:
return not self.IsAligned
@property
def SatisfiesAScopingRuleProperty(self) -> bool:
assert self.HasSemanticData
return any([
not self.IsHygienic,
self.IsInvokedWhereModifiableValueRequired,
self.IsInvokedWhereAddressableValueRequired,
self.IsAnyArgumentExpandedWhereModifiableValueRequired,
self.IsAnyArgumentExpandedWhereAddressableValueRequired,
self.DoesBodyReferenceMacroDefinedAfterMacro,
self.DoesBodyReferenceDeclDeclaredAfterMacro,
self.DoesSubexpressionExpandedFromBodyHaveLocalType,
self.DoesSubexpressionExpandedFromBodyHaveTypeDefinedAfterMacro,
self.IsAnyArgumentTypeDefinedAfterMacro,
self.IsAnyArgumentTypeLocalType,
])
@property
def SatisfiesATypingProperty(self) -> bool:
assert self.HasSemanticData
return any([
self.IsExpansionTypeAnonymous,
self.IsAnyArgumentTypeAnonymous,
self.DoesSubexpressionExpandedFromBodyHaveLocalType,
self.IsAnyArgumentTypeDefinedAfterMacro,
self.DoesSubexpressionExpandedFromBodyHaveTypeDefinedAfterMacro,
self.IsAnyArgumentTypeVoid,
(self.IsObjectLike and self.IsExpansionTypeVoid),
self.IsAnyArgumentTypeLocalType
])
@property
def SatisfiesACallingConventionProperty(self) -> bool:
assert self.HasSemanticData
return any([
self.DoesAnyArgumentHaveSideEffects,
self.IsAnyArgumentConditionallyEvaluated,
])
@property
def SatisfiesALanguageSpecificProperty(self) -> bool:
return self.MustUseMetaprogrammingToTransform
@property
def IsExpression(self) -> bool:
return self.ASTKind == 'Expr'
@property
def IsStatement(self) -> bool:
return self.ASTKind == 'Stmt'
def CanBeTurnedIntoEnumWithIntSize(self, int_size: IntSize) -> bool:
return self.CanBeTurnedIntoEnum and \
(self.IsICERepresentableByInt32 if int_size == IntSize.Int32
else self.IsICERepresentableByInt16)
@property
def IsValidStatementKind(self) -> bool:
if self.IsObjectLike:
return self.IsExpression
else:
return self.IsExpression or self.IsStatement
@property
def IsCalledByName(self) -> bool:
return any([
self.IsAnyArgumentConditionallyEvaluated,
self.DoesAnyArgumentHaveSideEffects,
])
@property
def ArgumentsCaptureEnvironment(self) -> bool:
return any([
self.IsAnyArgumentTypeAnonymous,
self.IsAnyArgumentTypeLocalType,
self.IsAnyArgumentTypeDefinedAfterMacro,
])
MacroMap = dict[Macro, Set[Invocation]]
@dataclass
class PreprocessorData:
mm: MacroMap = field(default_factory=dict)
inspected_macro_names: Set[str] = field(default_factory=set)
local_includes: Set[str] = field(default_factory=set)