@@ -30,8 +30,28 @@ def test_incompatible_types():
3030 x .field = str ("Hello" ) # $str=field str SPURIOUS: int=field int
3131 expects_string (x ) # $ str=field SPURIOUS: int=field
3232
33-
33+ # set in different function
34+ def set_foo (some_class_instance ): # $ tracked=foo
35+ some_class_instance .foo = tracked # $ tracked=foo tracked
36+
37+ def test_set_x ():
38+ x = SomeClass () # $ MISSING: tracked=foo
39+ set_foo (x ) # $ MISSING: tracked=foo
40+ print (x .foo ) # $ MISSING: tracked=foo tracked
41+
42+ # return from a different function
43+ def create_with_foo ():
44+ x = SomeClass () # $ tracked=foo
45+ x .foo = tracked # $ tracked=foo tracked
46+ return x # $ tracked=foo
47+
48+ def test_create_with_foo ():
49+ x = create_with_foo () # $ tracked=foo
50+ print (x .foo ) # $ tracked=foo tracked
51+
52+ # ------------------------------------------------------------------------------
3453# Attributes assigned statically to a class
54+ # ------------------------------------------------------------------------------
3555
3656class MyClass : # $tracked=field
3757 field = tracked # $tracked
@@ -40,7 +60,9 @@ class MyClass: # $tracked=field
4060instance = MyClass () # $tracked=field
4161lookup2 = instance .field # MISSING: tracked
4262
43- ## Dynamic attribute access
63+ # ------------------------------------------------------------------------------
64+ # Dynamic attribute access
65+ # ------------------------------------------------------------------------------
4466
4567# Via `getattr`/`setattr`
4668
@@ -99,3 +121,41 @@ def dunder_dict_indirect_read():
99121 do_stuff (y ) # $ MISSING: tracked
100122
101123
124+ # ------------------------------------------------------------------------------
125+ # Tracking of attribute on class instance
126+ # ------------------------------------------------------------------------------
127+
128+ # attribute set in method
129+ # inspired by https://github.com/github/codeql/pull/6023
130+
131+ class MyClass2 (object ):
132+ def __init__ (self ): # $ tracked=foo
133+ self .foo = tracked # $ tracked=foo tracked
134+
135+ def print_foo (self ): # $ MISSING: tracked=foo
136+ print (self .foo ) # $ MISSING: tracked=foo tracked
137+
138+ def possibly_uncalled_method (self ): # $ MISSING: tracked=foo
139+ print (self .foo ) # $ MISSING: tracked=foo tracked
140+
141+ instance = MyClass2 ()
142+ print (instance .foo ) # $ MISSING: tracked=foo tracked
143+ instance .print_foo () # $ MISSING: tracked=foo
144+
145+
146+ # attribute set from outside of class
147+
148+ class MyClass3 (object ):
149+ def print_self (self ): # $ tracked=foo
150+ print (self ) # $ tracked=foo
151+
152+ def print_foo (self ): # $ tracked=foo
153+ print (self .foo ) # $ tracked=foo tracked
154+
155+ def possibly_uncalled_method (self ): # $ MISSING: tracked=foo
156+ print (self .foo ) # $ MISSING: tracked=foo tracked
157+
158+ instance = MyClass3 () # $ tracked=foo
159+ instance .print_self () # $ tracked=foo
160+ instance .foo = tracked # $ tracked=foo tracked
161+ instance .print_foo () # $ tracked=foo
0 commit comments