@@ -51,6 +51,10 @@ class RootElementHasChildren(Error):
5151 pass
5252
5353
54+ class NoClasses (Error ):
55+ pass
56+
57+
5458def get_ql_property (cls : schema .Class , prop : schema .Property , prev_child : str = "" ) -> ql .Property :
5559 args = dict (
5660 type = prop .type if not prop .is_predicate else "predicate" ,
@@ -103,7 +107,7 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
103107 bases = cls .bases ,
104108 final = not cls .derived ,
105109 properties = properties ,
106- dir = cls .dir ,
110+ dir = pathlib . Path ( cls .group or "" ) ,
107111 ipa = bool (cls .ipa ),
108112 ** pragmas ,
109113 )
@@ -127,7 +131,7 @@ def get_ql_ipa_class_db(name: str) -> ql.Synth.FinalClassDb:
127131def get_ql_ipa_class (cls : schema .Class ):
128132 if cls .derived :
129133 return ql .Synth .NonFinalClass (name = cls .name , derived = sorted (cls .derived ),
130- root = ( cls .name == schema . root_class_name ) )
134+ root = not cls .bases )
131135 if cls .ipa and cls .ipa .from_class is not None :
132136 source = cls .ipa .from_class
133137 get_ql_ipa_class_db (source ).subtract_type (cls .name )
@@ -253,12 +257,14 @@ def generate(opts, renderer):
253257 existing |= {q for q in test_out .rglob ("*.ql" )}
254258 existing |= {q for q in test_out .rglob (missing_test_source_filename )}
255259
256- data = schema .load (input )
260+ data = schema .load_file (input )
257261
258262 classes = {name : get_ql_class (cls , data .classes ) for name , cls in data .classes .items ()}
259- # element root is absent in tests
260- if schema .root_class_name in classes and classes [schema .root_class_name ].has_children :
261- raise RootElementHasChildren
263+ if not classes :
264+ raise NoClasses
265+ root = next (iter (classes .values ()))
266+ if root .has_children :
267+ raise RootElementHasChildren (root )
262268
263269 imports = {}
264270
@@ -288,10 +294,10 @@ def generate(opts, renderer):
288294 for c in data .classes .values ():
289295 if _should_skip_qltest (c , data .classes ):
290296 continue
291- test_dir = test_out / c .dir / c .name
297+ test_dir = test_out / c .group / c .name
292298 test_dir .mkdir (parents = True , exist_ok = True )
293299 if not any (test_dir .glob ("*.swift" )):
294- log .warning (f"no test source in { c . dir / c . name } " )
300+ log .warning (f"no test source in { test_dir . relative_to ( test_out ) } " )
295301 renderer .render (ql .MissingTestInstructions (),
296302 test_dir / missing_test_source_filename )
297303 continue
@@ -308,12 +314,12 @@ def generate(opts, renderer):
308314 constructor_imports = []
309315 ipa_constructor_imports = []
310316 stubs = {}
311- for cls in sorted (data .classes .values (), key = lambda cls : (cls .dir , cls .name )):
317+ for cls in sorted (data .classes .values (), key = lambda cls : (cls .group , cls .name )):
312318 ipa_type = get_ql_ipa_class (cls )
313319 if ipa_type .is_final :
314320 final_ipa_types .append (ipa_type )
315321 if ipa_type .has_params :
316- stub_file = stub_out / cls .dir / f"{ cls .name } Constructor.qll"
322+ stub_file = stub_out / cls .group / f"{ cls .name } Constructor.qll"
317323 if not stub_file .is_file () or _is_generated_stub (stub_file ):
318324 # stub rendering must be postponed as we might not have yet all subtracted ipa types in `ipa_type`
319325 stubs [stub_file ] = ql .Synth .ConstructorStub (ipa_type )
@@ -326,7 +332,7 @@ def generate(opts, renderer):
326332
327333 for stub_file , data in stubs .items ():
328334 renderer .render (data , stub_file )
329- renderer .render (ql .Synth .Types (schema . root_class_name , final_ipa_types , non_final_ipa_types ), out / "Synth.qll" )
335+ renderer .render (ql .Synth .Types (root . name , final_ipa_types , non_final_ipa_types ), out / "Synth.qll" )
330336 renderer .render (ql .ImportList (constructor_imports ), out / "SynthConstructors.qll" )
331337 renderer .render (ql .ImportList (ipa_constructor_imports ), out / "PureSynthConstructors.qll" )
332338
0 commit comments