@@ -43,10 +43,6 @@ class FormatError(Error):
43
43
pass
44
44
45
45
46
- class ModifiedStubMarkedAsGeneratedError (Error ):
47
- pass
48
-
49
-
50
46
class RootElementHasChildren (Error ):
51
47
pass
52
48
@@ -216,32 +212,11 @@ def get_classes_used_by(cls: ql.Class) -> typing.List[str]:
216
212
return sorted (set (t for t in get_types_used_by (cls ) if t [0 ].isupper () and t != cls .name ))
217
213
218
214
219
- _generated_stub_re = re .compile (r"\n*private import .*\n+class \w+ extends Generated::\w+ \{[ \n]?\}" , re .MULTILINE )
220
-
221
-
222
- def _is_generated_stub (file : pathlib .Path ) -> bool :
223
- with open (file ) as contents :
224
- for line in contents :
225
- if not line .startswith ("// generated" ):
226
- return False
227
- break
228
- else :
229
- # no lines
230
- return False
231
- # we still do not detect modified synth constructors
232
- if not file .name .endswith ("Constructor.qll" ):
233
- # one line already read, if we can read 5 other we are past the normal stub generation
234
- line_threshold = 5
235
- first_lines = list (itertools .islice (contents , line_threshold ))
236
- if len (first_lines ) == line_threshold or not _generated_stub_re .match ("" .join (first_lines )):
237
- raise ModifiedStubMarkedAsGeneratedError (
238
- f"{ file .name } stub was modified but is still marked as generated" )
239
- return True
240
-
241
-
242
215
def format (codeql , files ):
243
- format_cmd = [codeql , "query" , "format" , "--in-place" , "--" ]
244
- format_cmd .extend (str (f ) for f in files if f .suffix in (".qll" , ".ql" ))
216
+ ql_files = [str (f ) for f in files if f .suffix in (".qll" , ".ql" )]
217
+ if not ql_files :
218
+ return
219
+ format_cmd = [codeql , "query" , "format" , "--in-place" , "--" ] + ql_files
245
220
res = subprocess .run (format_cmd , stderr = subprocess .PIPE , text = True )
246
221
if res .returncode :
247
222
for line in res .stderr .splitlines ():
@@ -307,11 +282,14 @@ def generate(opts, renderer):
307
282
stub_out = opts .ql_stub_output
308
283
test_out = opts .ql_test_output
309
284
missing_test_source_filename = "MISSING_SOURCE.txt"
285
+ include_file = stub_out .with_suffix (".qll" )
286
+
287
+ generated = {q for q in out .rglob ("*.qll" )}
288
+ generated .add (include_file )
289
+ generated .update (q for q in test_out .rglob ("*.ql" ))
290
+ generated .update (q for q in test_out .rglob (missing_test_source_filename ))
310
291
311
- existing = {q for q in out .rglob ("*.qll" )}
312
- existing |= {q for q in stub_out .rglob ("*.qll" ) if _is_generated_stub (q )}
313
- existing |= {q for q in test_out .rglob ("*.ql" )}
314
- existing |= {q for q in test_out .rglob (missing_test_source_filename )}
292
+ stubs = {q for q in stub_out .rglob ("*.qll" )}
315
293
316
294
data = schema .load_file (input )
317
295
@@ -324,77 +302,75 @@ def generate(opts, renderer):
324
302
325
303
imports = {}
326
304
327
- db_classes = [cls for cls in classes .values () if not cls .ipa ]
328
- renderer .render (ql .DbClasses (db_classes ), out / "Raw.qll" )
329
-
330
- classes_by_dir_and_name = sorted (classes .values (), key = lambda cls : (cls .dir , cls .name ))
331
- for c in classes_by_dir_and_name :
332
- imports [c .name ] = get_import (stub_out / c .path , opts .swift_dir )
333
-
334
- for c in classes .values ():
335
- qll = out / c .path .with_suffix (".qll" )
336
- c .imports = [imports [t ] for t in get_classes_used_by (c )]
337
- renderer .render (c , qll )
338
- stub_file = stub_out / c .path .with_suffix (".qll" )
339
- if not stub_file .is_file () or _is_generated_stub (stub_file ):
340
- stub = ql .Stub (
341
- name = c .name , base_import = get_import (qll , opts .swift_dir ))
342
- renderer .render (stub , stub_file )
343
-
344
- # for example path/to/elements -> path/to/elements.qll
345
- include_file = stub_out .with_suffix (".qll" )
346
- renderer .render (ql .ImportList (list (imports .values ())), include_file )
347
-
348
- renderer .render (ql .GetParentImplementation (list (classes .values ())), out / 'ParentChild.qll' )
349
-
350
- for c in data .classes .values ():
351
- if _should_skip_qltest (c , data .classes ):
352
- continue
353
- test_dir = test_out / c .group / c .name
354
- test_dir .mkdir (parents = True , exist_ok = True )
355
- if not any (test_dir .glob ("*.swift" )):
356
- log .warning (f"no test source in { test_dir .relative_to (test_out )} " )
357
- renderer .render (ql .MissingTestInstructions (),
358
- test_dir / missing_test_source_filename )
359
- continue
360
- total_props , partial_props = _partition (_get_all_properties_to_be_tested (c , data .classes ),
361
- lambda p : p .is_single or p .is_predicate )
362
- renderer .render (ql .ClassTester (class_name = c .name ,
363
- properties = total_props ,
364
- # in case of collapsed hierarchies we want to see the actual QL class in results
365
- show_ql_class = "qltest_collapse_hierarchy" in c .pragmas ),
366
- test_dir / f"{ c .name } .ql" )
367
- for p in partial_props :
368
- renderer .render (ql .PropertyTester (class_name = c .name ,
369
- property = p ), test_dir / f"{ c .name } _{ p .getter } .ql" )
370
-
371
- final_ipa_types = []
372
- non_final_ipa_types = []
373
- constructor_imports = []
374
- ipa_constructor_imports = []
375
- stubs = {}
376
- for cls in sorted (data .classes .values (), key = lambda cls : (cls .group , cls .name )):
377
- ipa_type = get_ql_ipa_class (cls )
378
- if ipa_type .is_final :
379
- final_ipa_types .append (ipa_type )
380
- if ipa_type .has_params :
381
- stub_file = stub_out / cls .group / f"{ cls .name } Constructor.qll"
382
- if not stub_file .is_file () or _is_generated_stub (stub_file ):
383
- # stub rendering must be postponed as we might not have yet all subtracted ipa types in `ipa_type`
384
- stubs [stub_file ] = ql .Synth .ConstructorStub (ipa_type )
385
- constructor_import = get_import (stub_file , opts .swift_dir )
386
- constructor_imports .append (constructor_import )
387
- if ipa_type .is_ipa :
388
- ipa_constructor_imports .append (constructor_import )
389
- else :
390
- non_final_ipa_types .append (ipa_type )
391
-
392
- for stub_file , data in stubs .items ():
393
- renderer .render (data , stub_file )
394
- renderer .render (ql .Synth .Types (root .name , final_ipa_types , non_final_ipa_types ), out / "Synth.qll" )
395
- renderer .render (ql .ImportList (constructor_imports ), out / "SynthConstructors.qll" )
396
- renderer .render (ql .ImportList (ipa_constructor_imports ), out / "PureSynthConstructors.qll" )
397
-
398
- renderer .cleanup (existing )
399
- if opts .ql_format :
400
- format (opts .codeql_binary , renderer .written )
305
+ with renderer .manage (generated = generated , stubs = stubs , registry = opts .generated_registry ) as renderer :
306
+
307
+ db_classes = [cls for cls in classes .values () if not cls .ipa ]
308
+ renderer .render (ql .DbClasses (db_classes ), out / "Raw.qll" )
309
+
310
+ classes_by_dir_and_name = sorted (classes .values (), key = lambda cls : (cls .dir , cls .name ))
311
+ for c in classes_by_dir_and_name :
312
+ imports [c .name ] = get_import (stub_out / c .path , opts .swift_dir )
313
+
314
+ for c in classes .values ():
315
+ qll = out / c .path .with_suffix (".qll" )
316
+ c .imports = [imports [t ] for t in get_classes_used_by (c )]
317
+ renderer .render (c , qll )
318
+ stub_file = stub_out / c .path .with_suffix (".qll" )
319
+ if not renderer .is_customized_stub (stub_file ):
320
+ stub = ql .Stub (name = c .name , base_import = get_import (qll , opts .swift_dir ))
321
+ renderer .render (stub , stub_file )
322
+
323
+ # for example path/to/elements -> path/to/elements.qll
324
+ renderer .render (ql .ImportList (list (imports .values ())), include_file )
325
+
326
+ renderer .render (ql .GetParentImplementation (list (classes .values ())), out / 'ParentChild.qll' )
327
+
328
+ for c in data .classes .values ():
329
+ if _should_skip_qltest (c , data .classes ):
330
+ continue
331
+ test_dir = test_out / c .group / c .name
332
+ test_dir .mkdir (parents = True , exist_ok = True )
333
+ if not any (test_dir .glob ("*.swift" )):
334
+ log .warning (f"no test source in { test_dir .relative_to (test_out )} " )
335
+ renderer .render (ql .MissingTestInstructions (),
336
+ test_dir / missing_test_source_filename )
337
+ continue
338
+ total_props , partial_props = _partition (_get_all_properties_to_be_tested (c , data .classes ),
339
+ lambda p : p .is_single or p .is_predicate )
340
+ renderer .render (ql .ClassTester (class_name = c .name ,
341
+ properties = total_props ,
342
+ # in case of collapsed hierarchies we want to see the actual QL class in results
343
+ show_ql_class = "qltest_collapse_hierarchy" in c .pragmas ),
344
+ test_dir / f"{ c .name } .ql" )
345
+ for p in partial_props :
346
+ renderer .render (ql .PropertyTester (class_name = c .name ,
347
+ property = p ), test_dir / f"{ c .name } _{ p .getter } .ql" )
348
+
349
+ final_ipa_types = []
350
+ non_final_ipa_types = []
351
+ constructor_imports = []
352
+ ipa_constructor_imports = []
353
+ stubs = {}
354
+ for cls in sorted (data .classes .values (), key = lambda cls : (cls .group , cls .name )):
355
+ ipa_type = get_ql_ipa_class (cls )
356
+ if ipa_type .is_final :
357
+ final_ipa_types .append (ipa_type )
358
+ if ipa_type .has_params :
359
+ stub_file = stub_out / cls .group / f"{ cls .name } Constructor.qll"
360
+ if not renderer .is_customized_stub (stub_file ):
361
+ # stub rendering must be postponed as we might not have yet all subtracted ipa types in `ipa_type`
362
+ stubs [stub_file ] = ql .Synth .ConstructorStub (ipa_type )
363
+ constructor_import = get_import (stub_file , opts .swift_dir )
364
+ constructor_imports .append (constructor_import )
365
+ if ipa_type .is_ipa :
366
+ ipa_constructor_imports .append (constructor_import )
367
+ else :
368
+ non_final_ipa_types .append (ipa_type )
369
+
370
+ for stub_file , data in stubs .items ():
371
+ renderer .render (data , stub_file )
372
+ renderer .render (ql .Synth .Types (root .name , final_ipa_types , non_final_ipa_types ), out / "Synth.qll" )
373
+ renderer .render (ql .ImportList (constructor_imports ), out / "SynthConstructors.qll" )
374
+ renderer .render (ql .ImportList (ipa_constructor_imports ), out / "PureSynthConstructors.qll" )
375
+ if opts .ql_format :
376
+ format (opts .codeql_binary , renderer .written )
0 commit comments