@@ -80,6 +80,28 @@ def get_ql_class(cls: schema.Class):
8080 )
8181
8282
83+ def _to_db_type (x : str ) -> str :
84+ if x [0 ].isupper ():
85+ return "@" + inflection .underscore (x )
86+ return x
87+
88+
89+ _final_db_class_lookup = {}
90+
91+
92+ def get_ql_ipa_class (cls : schema .Class ):
93+ if cls .derived :
94+ return ql .Ipa .NonFinalClass (name = cls .name , derived = sorted (cls .derived ))
95+ if cls .ipa and cls .ipa .from_class :
96+ source = cls .ipa .from_class
97+ _final_db_class_lookup .setdefault (source , ql .Ipa .FinalClassDb (source )).subtract_type (cls .name )
98+ return ql .Ipa .FinalClassIpaFrom (name = cls .name , type = _to_db_type (source ))
99+ if cls .ipa and cls .ipa .on_arguments :
100+ return ql .Ipa .FinalClassIpaOn (name = cls .name ,
101+ params = [ql .Ipa .Param (k , _to_db_type (v )) for k , v in cls .ipa .on_arguments .items ()])
102+ return _final_db_class_lookup .setdefault (cls .name , ql .Ipa .FinalClassDb (cls .name ))
103+
104+
83105def get_import (file : pathlib .Path , swift_dir : pathlib .Path ):
84106 stem = file .relative_to (swift_dir / "ql/lib" ).with_suffix ("" )
85107 return str (stem ).replace ("/" , "." )
@@ -96,7 +118,9 @@ def get_classes_used_by(cls: ql.Class):
96118 return sorted (set (t for t in get_types_used_by (cls ) if t [0 ].isupper ()))
97119
98120
99- _generated_stub_re = re .compile (r"private import .*\n\nclass \w+ extends \w+ \{[ \n]\}" , re .MULTILINE )
121+ _generated_stub_re = re .compile (r"\n*private import .*\n+class \w+ extends \w+ \{[ \n]?\}"
122+ "|"
123+ r"\n*predicate construct\w+\(.*?\) \{ none\(\) \}" , re .MULTILINE )
100124
101125
102126def _is_generated_stub (file ):
@@ -112,6 +136,7 @@ def _is_generated_stub(file):
112136 line_threshold = 5
113137 first_lines = list (itertools .islice (contents , line_threshold ))
114138 if len (first_lines ) == line_threshold or not _generated_stub_re .match ("" .join (first_lines )):
139+ print ("" .join (first_lines ))
115140 raise ModifiedStubMarkedAsGeneratedError (
116141 f"{ file .name } stub was modified but is still marked as generated" )
117142 return True
@@ -149,12 +174,14 @@ def _get_all_properties_to_be_tested(cls: ql.Class, lookup: typing.Dict[str, ql.
149174 yield ql .PropertyForTest (p .getter , p .type , p .is_single , p .is_predicate , p .is_repeated )
150175
151176
177+ def _partition_iter (x , pred ):
178+ x1 , x2 = itertools .tee (x )
179+ return filter (pred , x1 ), itertools .filterfalse (pred , x2 )
180+
181+
152182def _partition (l , pred ):
153183 """ partitions a list according to boolean predicate """
154- res = ([], [])
155- for x in l :
156- res [not pred (x )].append (x )
157- return res
184+ return map (list , _partition_iter (l , pred ))
158185
159186
160187def _is_in_qltest_collapsed_hierachy (cls : ql .Class , lookup : typing .Dict [str , ql .Class ]):
@@ -184,10 +211,10 @@ def generate(opts, renderer):
184211 existing |= {q for q in test_out .rglob (missing_test_source_filename )}
185212
186213 data = schema .load (input )
214+ data .classes .sort (key = lambda cls : (cls .dir , cls .name ))
187215
188216 classes = [get_ql_class (cls ) for cls in data .classes ]
189217 lookup = {cls .name : cls for cls in classes }
190- classes .sort (key = lambda cls : (cls .dir , cls .name ))
191218 imports = {}
192219
193220 for c in classes :
@@ -228,6 +255,24 @@ def generate(opts, renderer):
228255 renderer .render (ql .PropertyTester (class_name = c .name ,
229256 property = p ), test_dir / f"{ c .name } _{ p .getter } .ql" )
230257
258+ final_ipa_types = []
259+ non_final_ipa_types = []
260+ constructor_imports = []
261+ for cls in data .classes :
262+ ipa_type = get_ql_ipa_class (cls )
263+ if ipa_type .is_final :
264+ final_ipa_types .append (ipa_type )
265+ if ipa_type .is_ipa :
266+ stub_file = stub_out / cls .dir / f"{ cls .name } Constructor.qll"
267+ if not stub_file .is_file () or _is_generated_stub (stub_file ):
268+ renderer .render (ql .Ipa .ConstructorStub (ipa_type ), stub_file )
269+ constructor_imports .append (get_import (stub_file , opts .swift_dir ))
270+ else :
271+ non_final_ipa_types .append (ipa_type )
272+
273+ renderer .render (ql .Ipa .Types (schema .root_class_name , final_ipa_types , non_final_ipa_types ), out / "IpaTypes.qll" )
274+ renderer .render (ql .ImportList (constructor_imports ), out / "IpaConstructors.qll" )
275+
231276 renderer .cleanup (existing )
232277 if opts .ql_format :
233278 format (opts .codeql_binary , renderer .written )
0 commit comments