Skip to content

Commit 1365d09

Browse files
committed
Swift: generalize synth constructors
Now all types get a default synth constructor.
1 parent 8ba330a commit 1365d09

File tree

241 files changed

+1548
-274
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

241 files changed

+1548
-274
lines changed

swift/codegen/generators/qlgen.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,16 @@ def get_ql_ipa_class(cls: schema.Class):
9696
if cls.ipa and cls.ipa.from_class is not None:
9797
source = cls.ipa.from_class
9898
_final_db_class_lookup.setdefault(source, ql.Synth.FinalClassDb(source)).subtract_type(cls.name)
99-
return ql.Synth.FinalClassDerivedIpa(name=cls.name, params=[ql.Synth.Param("id", _to_db_type(source))])
99+
return ql.Synth.FinalClassDerivedIpa(name=cls.name,
100+
params=[ql.Synth.Param("id", _to_db_type(source))])
100101
if cls.ipa and cls.ipa.on_arguments is not None:
101-
return ql.Synth.FinalClassFreshIpa(name=cls.name, params=[ql.Synth.Param(k, _to_db_type(v)) for k, v in
102-
cls.ipa.on_arguments.items()])
103-
return _final_db_class_lookup.setdefault(cls.name, ql.Synth.FinalClassDb(cls.name))
102+
return ql.Synth.FinalClassFreshIpa(name=cls.name,
103+
params=[ql.Synth.Param(k, _to_db_type(v))
104+
for k, v in cls.ipa.on_arguments.items()])
105+
ret = ql.Synth.FinalClassDb(name=cls.name,
106+
params=[ql.Synth.Param("id", _to_db_type(cls.name))])
107+
_final_db_class_lookup[cls.name] = ret
108+
return ret
104109

105110

106111
def get_import(file: pathlib.Path, swift_dir: pathlib.Path):
@@ -119,14 +124,10 @@ def get_classes_used_by(cls: ql.Class):
119124
return sorted(set(t for t in get_types_used_by(cls) if t[0].isupper()))
120125

121126

122-
_generated_stub_re = re.compile(r"\n*private import .*\n+("
123-
r"class \w+ extends \w+ \{[ \n]?\}"
124-
"|"
125-
r"predicate construct\w+\(.*?\) \{ none\(\) \}"
126-
")", re.MULTILINE)
127+
_generated_stub_re = re.compile(r"\n*private import .*\n+class \w+ extends \w+ \{[ \n]?\}", re.MULTILINE)
127128

128129

129-
def _is_generated_stub(file):
130+
def _is_generated_stub(file: pathlib.Path) -> bool:
130131
with open(file) as contents:
131132
for line in contents:
132133
if not line.startswith("// generated"):
@@ -135,12 +136,14 @@ def _is_generated_stub(file):
135136
else:
136137
# no lines
137138
return False
138-
# one line already read, if we can read 5 other we are past the normal stub generation
139-
line_threshold = 5
140-
first_lines = list(itertools.islice(contents, line_threshold))
141-
if len(first_lines) == line_threshold or not _generated_stub_re.match("".join(first_lines)):
142-
raise ModifiedStubMarkedAsGeneratedError(
143-
f"{file.name} stub was modified but is still marked as generated")
139+
# we still do not detect modified synth constructors
140+
if not file.name.endswith("Constructor.qll"):
141+
# one line already read, if we can read 5 other we are past the normal stub generation
142+
line_threshold = 5
143+
first_lines = list(itertools.islice(contents, line_threshold))
144+
if len(first_lines) == line_threshold or not _generated_stub_re.match("".join(first_lines)):
145+
raise ModifiedStubMarkedAsGeneratedError(
146+
f"{file.name} stub was modified but is still marked as generated")
144147
return True
145148

146149

@@ -268,20 +271,24 @@ def generate(opts, renderer):
268271
final_ipa_types = []
269272
non_final_ipa_types = []
270273
constructor_imports = []
274+
ipa_constructor_imports = []
271275
for cls in sorted(data.classes.values(), key=lambda cls: (cls.dir, cls.name)):
272276
ipa_type = get_ql_ipa_class(cls)
273277
if ipa_type.is_final:
274278
final_ipa_types.append(ipa_type)
275-
if ipa_type.is_ipa and ipa_type.has_params:
276-
stub_file = stub_out / cls.dir / f"{cls.name}Constructor.qll"
277-
if not stub_file.is_file() or _is_generated_stub(stub_file):
278-
renderer.render(ql.Synth.ConstructorStub(ipa_type), stub_file)
279-
constructor_imports.append(get_import(stub_file, opts.swift_dir))
279+
stub_file = stub_out / cls.dir / f"{cls.name}Constructor.qll"
280+
if not stub_file.is_file() or _is_generated_stub(stub_file):
281+
renderer.render(ql.Synth.ConstructorStub(ipa_type), stub_file)
282+
constructor_import = get_import(stub_file, opts.swift_dir)
283+
constructor_imports.append(constructor_import)
284+
if ipa_type.is_ipa:
285+
ipa_constructor_imports.append(constructor_import)
280286
else:
281287
non_final_ipa_types.append(ipa_type)
282288

283289
renderer.render(ql.Synth.Types(schema.root_class_name, final_ipa_types, non_final_ipa_types), out / "Synth.qll")
284290
renderer.render(ql.ImportList(constructor_imports), out / "SynthConstructors.qll")
291+
renderer.render(ql.ImportList(ipa_constructor_imports), out / "PureSynthConstructors.qll")
285292

286293
renderer.cleanup(existing)
287294
if opts.ql_format:

swift/codegen/lib/ql.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,35 +166,37 @@ class Class:
166166
name: str
167167
first: bool = False
168168

169-
@dataclass
170-
class FinalClass(Class):
171-
is_final: ClassVar = True
172-
is_derived_ipa: ClassVar = False
173-
is_fresh_ipa: ClassVar = False
174-
is_db: ClassVar = False
175-
176-
@property
177-
def is_ipa(self):
178-
return self.is_fresh_ipa or self.is_derived_ipa
179-
180169
@dataclass
181170
class Param:
182171
param: str
183172
type: str
184173
first: bool = False
185174

186175
@dataclass
187-
class FinalClassIpa(FinalClass):
176+
class FinalClass(Class):
177+
is_final: ClassVar = True
178+
is_derived_ipa: ClassVar = False
179+
is_fresh_ipa: ClassVar = False
180+
is_db: ClassVar = False
181+
188182
params: List["Synth.Param"] = field(default_factory=list)
189183

190184
def __post_init__(self):
191185
if self.params:
192186
self.params[0].first = True
193187

188+
@property
189+
def is_ipa(self):
190+
return self.is_fresh_ipa or self.is_derived_ipa
191+
194192
@property
195193
def has_params(self) -> bool:
196194
return bool(self.params)
197195

196+
@dataclass
197+
class FinalClassIpa(FinalClass):
198+
pass
199+
198200
@dataclass
199201
class FinalClassDerivedIpa(FinalClassIpa):
200202
is_derived_ipa: ClassVar = True
@@ -246,4 +248,4 @@ def __post_init__(self):
246248
class ConstructorStub:
247249
template: ClassVar = "ql_ipa_constructor_stub"
248250

249-
cls: Union["Synth.FinalClassDerivedIpa", "Synth.FinalClassFreshIpa"]
251+
cls: "Synth.FinalClass"
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
// generated by {{generator}}, remove this comment if you wish to edit this file
22
private import codeql.swift.generated.Raw
3+
{{#cls}}
4+
{{#is_db}}
5+
{{#has_subtracted_ipa_types}}
6+
private import codeql.swift.PureSynthConstructors
7+
{{/has_subtracted_ipa_types}}
8+
{{/is_db}}
39

4-
predicate construct{{cls.name}}({{#cls.params}}{{^first}}, {{/first}}{{type}} {{param}}{{/cls.params}}) { none() }
10+
predicate construct{{name}}({{#params}}{{^first}}, {{/first}}{{type}} {{param}}{{/params}}) {
11+
{{#is_db}}
12+
{{#subtracted_ipa_types}}{{^first}} and {{/first}}not construct{{name}}(id){{/subtracted_ipa_types}}
13+
{{^subtracted_ipa_types}}any(){{/subtracted_ipa_types}}
14+
{{/is_db}}
15+
{{^is_db}}
16+
none()
17+
{{/is_db}}
18+
}
19+
{{/cls}}

swift/codegen/templates/ql_ipa_types.mustache

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@ cached module Synth {
77
{{^first}}
88
or
99
{{/first}}
10-
{{#is_ipa}}
1110
T{{name}}({{#params}}{{^first}}, {{/first}}{{type}} {{param}}{{/params}}){{#has_params}} { construct{{name}}({{#params}}{{^first}}, {{/first}}{{param}}{{/params}}) }{{/has_params}}
12-
{{/is_ipa}}
13-
{{#is_db}}
14-
T{{name}}(Raw::{{name}} id){{#has_subtracted_ipa_types}} { {{#subtracted_ipa_types}}{{^first}} and {{/first}}not construct{{name}}(id){{/subtracted_ipa_types}} }{{/has_subtracted_ipa_types}}
15-
{{/is_db}}
1611
{{/final_classes}}
1712

1813
{{#non_final_classes}}

swift/codegen/test/test_qlgen.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ def _filter_generated_classes(ret, output_test_files=False):
101101
str(f): ret[ql_test_output_path() / f]
102102
for f in test_files
103103
}
104-
base_files -= {pathlib.Path(f"{name}.qll") for name in ("Raw", "Synth", "SynthConstructors")}
105-
assert stub_files == base_files
104+
base_files -= {pathlib.Path(f"{name}.qll") for name in
105+
("Raw", "Synth", "SynthConstructors", "PureSynthConstructors")}
106+
assert base_files <= stub_files
106107
return {
107108
str(f): (ret[stub_path() / f], ret[ql_output_path() / f])
108109
for f in base_files
@@ -131,6 +132,7 @@ def test_empty(generate):
131132
children_file(): ql.GetParentImplementation(),
132133
ql_output_path() / "Synth.qll": ql.Synth.Types(schema.root_class_name),
133134
ql_output_path() / "SynthConstructors.qll": ql.ImportList(),
135+
ql_output_path() / "PureSynthConstructors.qll": ql.ImportList(),
134136
ql_output_path() / "Raw.qll": ql.DbClasses(),
135137
ql_output_path() / "Raw.qll": ql.DbClasses(),
136138
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
2+
private import codeql.swift.generated.Raw
3+
4+
predicate constructComment(Raw::Comment id) { any() }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
2+
private import codeql.swift.generated.Raw
3+
4+
predicate constructDbFile(Raw::DbFile id) { any() }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
2+
private import codeql.swift.generated.Raw
3+
4+
predicate constructDbLocation(Raw::DbLocation id) { any() }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
2+
private import codeql.swift.generated.Raw
3+
4+
predicate constructUnknownFile() { none() }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
2+
private import codeql.swift.generated.Raw
3+
4+
predicate constructUnknownLocation() { none() }

0 commit comments

Comments
 (0)