@@ -237,6 +237,124 @@ def set_attr(obj):
237237 SINK (y .attr ) # $ MISSING: flow
238238 SINK_F (z .attr ) # $ MISSING: flow
239239
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+ def test_inst_no_call (self ):
252+ SINK (self .my_tuple [0 ]) # $ MISSING: flow
253+ SINK_F (self .my_tuple [1 ])
254+
255+ @classmethod
256+ def test_cm (cls ):
257+ SINK (cls .my_tuple [0 ]) # $ flow="SOURCE, l:-12 -> cls.my_tuple[0]"
258+ SINK_F (cls .my_tuple [1 ])
259+
260+ @classmethod
261+ def test_cm_no_call (cls ):
262+ SINK (cls .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-8 -> cls.my_tuple[0]"
263+ SINK_F (cls .my_tuple [1 ])
264+
265+
266+ @expects (2 * 4 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
267+ def test_WithTuple ():
268+ SINK (WithTuple .my_tuple [0 ]) # $ flow="SOURCE, l:-23 -> WithTuple.my_tuple[0]"
269+ SINK_F (WithTuple .my_tuple [1 ])
270+
271+ WithTuple .test_cm ()
272+
273+ inst = WithTuple ()
274+ inst .test_inst ()
275+
276+ SINK (inst .my_tuple [0 ]) # $ MISSING: flow
277+ SINK_F (inst .my_tuple [1 ])
278+
279+
280+ @expects (4 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
281+ def test_inst_override ():
282+ inst = WithTuple ()
283+
284+ # setting attribute on instance does not override class attribute, it's only on the
285+ # instance!
286+ inst .my_tuple = (NONSOURCE , SOURCE )
287+
288+ SINK_F (inst .my_tuple [0 ])
289+ SINK (inst .my_tuple [1 ]) # $ flow="SOURCE, l:-3 -> inst.my_tuple[1]"
290+
291+ SINK (WithTuple .my_tuple [0 ]) # $ flow="SOURCE, l:-46 -> WithTuple.my_tuple[0]"
292+ SINK_F (WithTuple .my_tuple [1 ])
293+
294+
295+ class WithTuple2 :
296+ my_tuple = (NONSOURCE ,)
297+
298+ def set_to_source ():
299+ WithTuple2 .my_tuple = (SOURCE ,)
300+
301+ @expects (4 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
302+ def test_global_flow_to_class_attribute ():
303+ inst = WithTuple2 ()
304+ SINK_F (WithTuple2 .my_tuple [0 ])
305+ SINK_F (inst .my_tuple [0 ])
306+
307+ set_to_source ()
308+
309+ SINK (WithTuple2 .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-10 -> WithTuple2.my_tuple[0]"
310+ SINK (inst .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-11 -> inst.my_tuple[0]"
311+
312+
313+ class Outer :
314+ src = SOURCE
315+ class Inner :
316+ src = SOURCE
317+
318+ @expects (2 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
319+ def test_nested_class ():
320+ SINK (Outer .src ) # $ flow="SOURCE, l:-6 -> Outer.src"
321+ SINK (Outer .Inner .src ) # $ flow="SOURCE, l:-5 -> Outer.Inner.src"
322+
323+ # --------------------------------------
324+ # unique classes from functions
325+ # --------------------------------------
326+ def make_class ():
327+ # a fresh class is returned each time this function is called
328+ class C :
329+ my_tuple = (NONSOURCE ,)
330+ return C
331+
332+ @expects (8 ) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
333+ def test_unique_class ():
334+ # This test highlights that if we use the _ClassExpr_ itself as the target/source
335+ # for jumpsteps, we will end up with spurious flow (that is, we will think that
336+ # x_cls and y_cls are the same, so by updating .my_tuple on x_cls we might propagate
337+ # that to y_cls as well -- it might not matter too much in reality, but certainly an
338+ # interesting corner case)
339+ x_cls = make_class ()
340+ y_cls = make_class ()
341+
342+ assert x_cls != y_cls
343+
344+ x_inst = x_cls ()
345+ y_inst = y_cls ()
346+
347+ SINK_F (x_cls .my_tuple [0 ])
348+ SINK_F (x_inst .my_tuple [0 ])
349+ SINK_F (y_cls .my_tuple [0 ])
350+ SINK_F (y_inst .my_tuple [0 ])
351+
352+ x_cls .my_tuple = (SOURCE ,)
353+ SINK (x_cls .my_tuple [0 ]) # $ flow="SOURCE, l:-1 -> x_cls.my_tuple[0]"
354+ SINK (x_inst .my_tuple [0 ]) # $ MISSING: flow="SOURCE, l:-2 -> x_inst.my_tuple[0]"
355+ SINK_F (y_cls .my_tuple [0 ])
356+ SINK_F (y_inst .my_tuple [0 ])
357+
240358# ------------------------------------------------------------------------------
241359# Crosstalk test -- using different function based on conditional
242360# ------------------------------------------------------------------------------
0 commit comments