Skip to content

Commit ff78ac9

Browse files
authored
Merge pull request github#13249 from github/redsun82/swift-hidden-ast
Swift: make only certain elements hideable in the AST
2 parents 7c5fef3 + 09ce29b commit ff78ac9

File tree

94 files changed

+417
-1413
lines changed

Some content is hidden

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

94 files changed

+417
-1413
lines changed

misc/codegen/generators/qlgen.py

Lines changed: 8 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,7 @@ 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,
167169
**pragmas,
168170
)
169171

@@ -254,7 +256,7 @@ def _get_all_properties_to_be_tested(cls: schema.Class, lookup: typing.Dict[str,
254256
for c, p in _get_all_properties(cls, lookup):
255257
if not ("qltest_skip" in c.pragmas or "qltest_skip" in p.pragmas):
256258
# TODO here operations are duplicated, but should be better if we split ql and qltest generation
257-
p = get_ql_property(c, p)
259+
p = get_ql_property(c, p, lookup)
258260
yield ql.PropertyForTest(p.getter, is_total=p.is_single or p.is_predicate,
259261
type=p.type if not p.is_predicate else None, is_indexed=p.is_indexed)
260262
if p.is_repeated and not p.is_optional:
@@ -329,7 +331,7 @@ def generate(opts, renderer):
329331

330332
data = schemaloader.load_file(input)
331333

332-
classes = {name: get_ql_class(cls) for name, cls in data.classes.items()}
334+
classes = {name: get_ql_class(cls, data.classes) for name, cls in data.classes.items()}
333335
if not classes:
334336
raise NoClasses
335337
root = next(iter(classes.values()))

misc/codegen/lib/ql.py

Lines changed: 2 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,7 @@ class Class:
113114
ql_internal: bool = False
114115
ipa: bool = False
115116
doc: List[str] = field(default_factory=list)
117+
hideable: bool = False
116118

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

misc/codegen/lib/schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class Class:
9191
"""^^^ filled with `True` for non-final classes with only synthesized final descendants """
9292
doc: List[str] = field(default_factory=list)
9393
default_doc_name: Optional[str] = None
94+
hideable: bool = False
9495

9596
@property
9697
def final(self):

misc/codegen/lib/schemadefs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ def f(cls: type) -> type:
145145
_Pragma("qltest_uncollapse_hierarchy")
146146

147147
ql.default_doc_name = lambda doc: _annotate(doc_name=doc)
148+
ql.hideable = _annotate(hideable=True)
148149
_Pragma("ql_internal")
149150

150151
_Pragma("cpp_skip")

misc/codegen/loaders/schemaloader.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def _get_class(cls: type) -> schema.Class:
3737
derived={d.__name__ for d in cls.__subclasses__()},
3838
# getattr to inherit from bases
3939
group=getattr(cls, "_group", ""),
40+
hideable=getattr(cls, "_hideable", False),
4041
# in the following we don't use `getattr` to avoid inheriting
4142
pragmas=cls.__dict__.get("_pragmas", []),
4243
ipa=cls.__dict__.get("_ipa", None),
@@ -94,6 +95,18 @@ def fill_is_ipa(name: str):
9495
cls.ipa = True
9596

9697

98+
def _fill_hideable_information(classes: typing.Dict[str, schema.Class]):
99+
""" Update the class map propagating the `hideable` attribute upwards in the hierarchy """
100+
todo = [cls for cls in classes.values() if cls.hideable]
101+
while todo:
102+
cls = todo.pop()
103+
for base in cls.bases:
104+
supercls = classes[base]
105+
if not supercls.hideable:
106+
supercls.hideable = True
107+
todo.append(supercls)
108+
109+
97110
def load(m: types.ModuleType) -> schema.Schema:
98111
includes = set()
99112
classes = {}
@@ -122,6 +135,7 @@ def load(m: types.ModuleType) -> schema.Schema:
122135
cls.is_null_class = True
123136

124137
_fill_ipa_information(classes)
138+
_fill_hideable_information(classes)
125139

126140
return schema.Schema(includes=includes, classes=_toposort_classes_by_group(classes), null=null)
127141

misc/codegen/templates/ql_class.mustache

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ module Generated {
6060
{{/final}}
6161
{{#properties}}
6262

63-
{{#type_is_class}}
63+
{{#type_is_hideable}}
6464
/**
6565
* {{>ql_property_doc}} *
6666
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
@@ -85,11 +85,11 @@ module Generated {
8585
*/
8686
final {{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
8787
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())
88+
{{#hideable}}if exists(this.getResolveStep()) then result = immediate else {{/hideable}}result = immediate.resolve())
8989
}
9090

91-
{{/type_is_class}}
92-
{{^type_is_class}}
91+
{{/type_is_hideable}}
92+
{{^type_is_hideable}}
9393
/**
9494
* {{>ql_property_doc}} *
9595
{{#has_description}}
@@ -100,14 +100,14 @@ module Generated {
100100
*/
101101
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
102102
{{^ipa}}
103-
{{^is_predicate}}result = {{/is_predicate}}Synth::convert{{name}}ToRaw(this){{^root}}.(Raw::{{name}}){{/root}}.{{getter}}({{#is_indexed}}index{{/is_indexed}})
103+
{{^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}}
104104
{{/ipa}}
105105
{{#ipa}}
106106
none()
107107
{{/ipa}}
108108
}
109109

110-
{{/type_is_class}}
110+
{{/type_is_hideable}}
111111
{{#is_optional}}
112112
/**
113113
* Holds if `{{getter}}({{#is_repeated}}index{{/is_repeated}})` exists.

misc/codegen/templates/ql_parent.mustache

Lines changed: 8 additions & 8 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}}
@@ -71,21 +71,21 @@ none()
7171
* if `e` has conversions, `getImmediateParent(e)` will give the innermost conversion in the hidden AST.
7272
*/
7373
Element getImmediateParent(Element e) {
74-
// `unique` is used here to tell the optimizer that there is in fact only one result
75-
// this is tested by the `library-tests/parent/no_double_parents.ql` test
76-
result = unique(Element x | e = Impl::getImmediateChild(x, _, _) | x)
74+
// `unique` is used here to tell the optimizer that there is in fact only one result
75+
// this is tested by the `library-tests/parent/no_double_parents.ql` test
76+
result = unique(Element x | e = Impl::getImmediateChild(x, _, _) | x)
7777
}
7878

7979
/**
8080
* Gets the immediate 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.
8181
*/
8282
Element getImmediateChildAndAccessor(Element e, int index, string accessor) {
83-
exists(string partialAccessor | result = Impl::getImmediateChild(e, index, partialAccessor) and accessor = "get" + partialAccessor)
83+
exists(string partialAccessor | result = Impl::getImmediateChild(e, index, partialAccessor) and accessor = "get" + partialAccessor)
8484
}
8585

8686
/**
8787
* 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.
8888
*/
8989
Element getChildAndAccessor(Element e, int index, string accessor) {
90-
exists(string partialAccessor | result = Impl::getImmediateChild(e, index, partialAccessor).resolve() and accessor = "get" + partialAccessor)
90+
exists(string partialAccessor | result = Impl::getImmediateChild(e, index, partialAccessor).resolve() and accessor = "get" + partialAccessor)
9191
}

0 commit comments

Comments
 (0)