@@ -237,6 +237,104 @@ def set_attr(obj):
237
237
SINK (y .attr ) # $ MISSING: flow
238
238
SINK_F (z .attr ) # $ MISSING: flow
239
239
240
+ # ------------------------------------------------------------------------------
241
+ # Content in class attribute
242
+ # ------------------------------------------------------------------------------
243
+
244
+ class WithTuple :
245
+ my_tuple = (SOURCE , NONSOURCE )
246
+
247
+ def test_inst (self ):
248
+ SINK (self .my_tuple [0 ]) # $ MISSING: flow
249
+ SINK_F (self .my_tuple [1 ])
250
+
251
+ @classmethod
252
+ def test_cm (cls ):
253
+ SINK (cls .my_tuple [0 ]) # $ MISSING: flow
254
+ SINK_F (cls .my_tuple [1 ])
255
+
256
+
257
+ @expects (2 * 4 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
258
+ def test_WithTuple ():
259
+ SINK (WithTuple .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-7 -> WithTuple.my_tuple[0]"
260
+ SINK_F (WithTuple .my_tuple [1 ])
261
+
262
+ WithTuple .test_cm ()
263
+
264
+ inst = WithTuple ()
265
+ inst .test_inst ()
266
+
267
+ SINK (inst .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-18 -> inst.my_tuple[0]"
268
+ SINK_F (inst .my_tuple [1 ])
269
+
270
+
271
+ @expects (4 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
272
+ def test_inst_override ():
273
+ inst = WithTuple ()
274
+
275
+ # setting attribute on instance does not override class attribute, it's only on the
276
+ # instance!
277
+ inst .my_tuple = (NONSOURCE , SOURCE )
278
+
279
+ SINK_F (inst .my_tuple [0 ])
280
+ SINK (inst .my_tuple [1 ]) # $ flow="SOURCE, l:-3 -> inst.my_tuple[1]"
281
+
282
+ SINK (WithTuple .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-27 -> WithTuple.my_tuple[0]"
283
+ SINK_F (WithTuple .my_tuple [1 ])
284
+
285
+
286
+ class WithTuple2 :
287
+ my_tuple = (NONSOURCE ,)
288
+
289
+ def set_to_source ():
290
+ WithTuple2 .my_tuple = (SOURCE ,)
291
+
292
+ @expects (4 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
293
+ def test_class_override ():
294
+ inst = WithTuple2 ()
295
+ SINK_F (WithTuple2 .my_tuple [0 ])
296
+ SINK_F (inst .my_tuple [0 ])
297
+
298
+ set_to_source ()
299
+
300
+ SINK (WithTuple2 .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-10 -> WithTuple2.my_tuple[0]"
301
+ SINK (inst .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-11 -> inst.my_tuple[0]"
302
+
303
+ # --------------------------------------
304
+ # unique classes from functions
305
+ # --------------------------------------
306
+ def make_class ():
307
+ # a fresh class is returned each time this function is called
308
+ class C :
309
+ my_tuple = (NONSOURCE ,)
310
+ return C
311
+
312
+ @expects (8 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
313
+ def test_unique_class ():
314
+ # This test highlights that if we use the _ClassExpr_ itself as the target/source
315
+ # for jumpsteps, we will end up with spurious flow (that is, we will think that
316
+ # x_cls and y_cls are the same, so by updating .my_tuple on x_cls we might propagate
317
+ # that to y_cls as well -- it might not matter too much in reality, but certainly an
318
+ # interesting corner case)
319
+ x_cls = make_class ()
320
+ y_cls = make_class ()
321
+
322
+ assert x_cls != y_cls
323
+
324
+ x_inst = x_cls ()
325
+ y_inst = y_cls ()
326
+
327
+ SINK_F (x_cls .my_tuple [0 ])
328
+ SINK_F (x_inst .my_tuple [0 ])
329
+ SINK_F (y_cls .my_tuple [0 ])
330
+ SINK_F (y_inst .my_tuple [0 ])
331
+
332
+ x_cls .my_tuple = (SOURCE ,)
333
+ SINK (x_cls .my_tuple [0 ]) # $ flow="SOURCE, l:-1 -> x_cls.my_tuple[0]"
334
+ SINK (x_inst .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-2 -> x_inst.my_tuple[0]"
335
+ SINK_F (y_cls .my_tuple [0 ])
336
+ SINK_F (y_inst .my_tuple [0 ])
337
+
240
338
# ------------------------------------------------------------------------------
241
339
# Crosstalk test -- using different function based on conditional
242
340
# ------------------------------------------------------------------------------
0 commit comments