Skip to content

Commit a087fef

Browse files
committed
Swift: implement @ql.hideable
1 parent 7dd18ff commit a087fef

File tree

8 files changed

+112
-74
lines changed

8 files changed

+112
-74
lines changed

misc/codegen/generators/qlgen.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,16 @@ def _get_doc(cls: schema.Class, prop: schema.Property, plural=None):
102102
return f"{prop_name} of this {class_name}"
103103

104104

105-
def get_ql_property(cls: schema.Class, prop: schema.Property, prev_child: str = "") -> ql.Property:
105+
def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dict[str, schema.Class], prev_child: str = "") -> ql.Property:
106106
args = dict(
107107
type=prop.type if not prop.is_predicate else "predicate",
108108
qltest_skip="qltest_skip" in prop.pragmas,
109109
prev_child=prev_child if prop.is_child else None,
110110
is_optional=prop.is_optional,
111111
is_predicate=prop.is_predicate,
112112
is_unordered=prop.is_unordered,
113-
description=prop.description
113+
description=prop.description,
114+
type_is_hideable=lookup[prop.type].hideable if prop.type in lookup else False,
114115
)
115116
if prop.is_single:
116117
args.update(
@@ -147,12 +148,12 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, prev_child: str =
147148
return ql.Property(**args)
148149

149150

150-
def get_ql_class(cls: schema.Class) -> ql.Class:
151+
def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> ql.Class:
151152
pragmas = {k: True for k in cls.pragmas if k.startswith("ql")}
152153
prev_child = ""
153154
properties = []
154155
for p in cls.properties:
155-
prop = get_ql_property(cls, p, prev_child)
156+
prop = get_ql_property(cls, p, lookup, prev_child)
156157
if prop.is_child:
157158
prev_child = prop.singular
158159
properties.append(prop)
@@ -164,6 +165,8 @@ def get_ql_class(cls: schema.Class) -> ql.Class:
164165
dir=pathlib.Path(cls.group or ""),
165166
ipa=bool(cls.ipa),
166167
doc=cls.doc,
168+
hideable=cls.hideable,
169+
hideable_root=cls.hideable_root,
167170
**pragmas,
168171
)
169172

@@ -254,7 +257,7 @@ def _get_all_properties_to_be_tested(cls: schema.Class, lookup: typing.Dict[str,
254257
for c, p in _get_all_properties(cls, lookup):
255258
if not ("qltest_skip" in c.pragmas or "qltest_skip" in p.pragmas):
256259
# TODO here operations are duplicated, but should be better if we split ql and qltest generation
257-
p = get_ql_property(c, p)
260+
p = get_ql_property(c, p, lookup)
258261
yield ql.PropertyForTest(p.getter, is_total=p.is_single or p.is_predicate,
259262
type=p.type if not p.is_predicate else None, is_indexed=p.is_indexed)
260263
if p.is_repeated and not p.is_optional:
@@ -329,7 +332,7 @@ def generate(opts, renderer):
329332

330333
data = schemaloader.load_file(input)
331334

332-
classes = {name: get_ql_class(cls) for name, cls in data.classes.items()}
335+
classes = {name: get_ql_class(cls, data.classes) for name, cls in data.classes.items()}
333336
if not classes:
334337
raise NoClasses
335338
root = next(iter(classes.values()))

misc/codegen/lib/ql.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class Property:
4242
description: List[str] = field(default_factory=list)
4343
doc: Optional[str] = None
4444
doc_plural: Optional[str] = None
45+
type_is_hideable: bool = False
4546

4647
def __post_init__(self):
4748
if self.tableparams:
@@ -113,6 +114,8 @@ class Class:
113114
ql_internal: bool = False
114115
ipa: bool = False
115116
doc: List[str] = field(default_factory=list)
117+
hideable_root: bool = False
118+
hideable: bool = False
116119

117120
def __post_init__(self):
118121
self.bases = [Base(str(b), str(prev)) for b, prev in zip(self.bases, itertools.chain([""], self.bases))]

misc/codegen/templates/ql_class.mustache

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ module Generated {
3737
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
3838
*/
3939
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
40-
40+
{{/root}}
41+
{{#hideable_root}}
4142
/**
4243
* Gets the most immediate element that should substitute this element in the explicit AST, if any.
4344
* Classes can override this to indicate this node should be in the "hidden" AST, mostly reserved
@@ -54,13 +55,13 @@ module Generated {
5455
or
5556
result = this.getResolveStep().resolve()
5657
}
57-
{{/root}}
58+
{{/hideable_root}}
5859
{{#final}}
5960
override string getAPrimaryQlClass() { result = "{{name}}" }
6061
{{/final}}
6162
{{#properties}}
6263

63-
{{#type_is_class}}
64+
{{#type_is_hideable}}
6465
/**
6566
* {{>ql_property_doc}} *
6667
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
@@ -85,11 +86,11 @@ module Generated {
8586
*/
8687
final {{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
8788
exists({{type}} immediate | immediate = this.get{{#is_unordered}}An{{/is_unordered}}Immediate{{singular}}({{#is_indexed}}index{{/is_indexed}}) and
88-
if exists(this.getResolveStep()) then result = immediate else result = immediate.resolve())
89+
{{#hideable}}if exists(this.getResolveStep()) then result = immediate else {{/hideable}}result = immediate.resolve())
8990
}
9091

91-
{{/type_is_class}}
92-
{{^type_is_class}}
92+
{{/type_is_hideable}}
93+
{{^type_is_hideable}}
9394
/**
9495
* {{>ql_property_doc}} *
9596
{{#has_description}}
@@ -100,14 +101,14 @@ module Generated {
100101
*/
101102
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
102103
{{^ipa}}
103-
{{^is_predicate}}result = {{/is_predicate}}Synth::convert{{name}}ToRaw(this){{^root}}.(Raw::{{name}}){{/root}}.{{getter}}({{#is_indexed}}index{{/is_indexed}})
104+
{{^is_predicate}}result = {{/is_predicate}}{{#type_is_class}}Synth::convert{{type}}FromRaw({{/type_is_class}}Synth::convert{{name}}ToRaw(this){{^root}}.(Raw::{{name}}){{/root}}.{{getter}}({{#is_indexed}}index{{/is_indexed}}){{#type_is_class}}){{/type_is_class}}
104105
{{/ipa}}
105106
{{#ipa}}
106107
none()
107108
{{/ipa}}
108109
}
109110

110-
{{/type_is_class}}
111+
{{/type_is_hideable}}
111112
{{#is_optional}}
112113
/**
113114
* Holds if `{{getter}}({{#is_repeated}}index{{/is_repeated}})` exists.

misc/codegen/templates/ql_parent.mustache

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private module Impl {
2828
{{! for single and optional properties it adds 1 (regardless of whether the optional property exists) }}
2929
{{! for repeated it adds 1 + the maximum index (which works for repeated optional as well) }}
3030
and
31-
n{{singular}} = n{{prev_child}} + 1{{#is_repeated}}+ max(int i | i = -1 or exists(e.getImmediate{{singular}}(i)) | i){{/is_repeated}}
31+
n{{singular}} = n{{prev_child}} + 1{{#is_repeated}}+ max(int i | i = -1 or exists(e.get{{#type_is_hideable}}Immediate{{/type_is_hideable}}{{singular}}(i)) | i){{/is_repeated}}
3232
{{/is_child}}
3333
{{/properties}} and (
3434
none()
@@ -40,10 +40,10 @@ private module Impl {
4040
{{#is_child}}
4141
or
4242
{{#is_repeated}}
43-
result = e.getImmediate{{singular}}(index - n{{prev_child}}) and partialPredicateCall = "{{singular}}(" + (index - n{{prev_child}}).toString() + ")"
43+
result = e.get{{#type_is_hideable}}Immediate{{/type_is_hideable}}{{singular}}(index - n{{prev_child}}) and partialPredicateCall = "{{singular}}(" + (index - n{{prev_child}}).toString() + ")"
4444
{{/is_repeated}}
4545
{{^is_repeated}}
46-
index = n{{prev_child}} and result = e.getImmediate{{singular}}() and partialPredicateCall = "{{singular}}()"
46+
index = n{{prev_child}} and result = e.get{{#type_is_hideable}}Immediate{{/type_is_hideable}}{{singular}}() and partialPredicateCall = "{{singular}}()"
4747
{{/is_repeated}}
4848
{{/is_child}}
4949
{{/properties}}
@@ -64,6 +64,10 @@ none()
6464
{{/final}}
6565
{{/classes}}
6666
}
67+
68+
Element resolve(Element e) {
69+
{{#classes}}{{#hideable_root}}if e instanceof {{name}} then result = e.({{name}}).resolve() else {{/hideable_root}}{{/classes}}result = e
70+
}
6771
}
6872

6973
/**
@@ -87,5 +91,5 @@ exists(string partialAccessor | result = Impl::getImmediateChild(e, index, parti
8791
* Gets the child indexed at `index`. Indexes are not guaranteed to be contiguous, but are guaranteed to be distinct. `accessor` is bound the member predicate call resulting in the given child.
8892
*/
8993
Element getChildAndAccessor(Element e, int index, string accessor) {
90-
exists(string partialAccessor | result = Impl::getImmediateChild(e, index, partialAccessor).resolve() and accessor = "get" + partialAccessor)
94+
exists(string partialAccessor | result = Impl::resolve(Impl::getImmediateChild(e, index, partialAccessor)) and accessor = "get" + partialAccessor)
9195
}

0 commit comments

Comments
 (0)