Skip to content

Commit ecce7f1

Browse files
authored
Merge pull request github#9380 from github/redsun82/swift-getparent
Swift: generate `getParent` implementation
2 parents 6f9e9e8 + 6b90b2b commit ecce7f1

36 files changed

+3000
-816
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repos:
2525

2626
- id: sync-files
2727
name: Fix files required to be identical
28-
files: \.(qll?|qhelp)$
28+
files: \.(qll?|qhelp|swift)$
2929
language: system
3030
entry: python3 config/sync-files.py --latest
3131
pass_filenames: false

config/identical-files.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,5 +566,21 @@
566566
"Typo database": [
567567
"javascript/ql/src/Expressions/TypoDatabase.qll",
568568
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
569+
],
570+
"Swift declarations test file": [
571+
"swift/ql/test/extractor-tests/declarations/declarations.swift",
572+
"swift/ql/test/library-tests/parent/declarations.swift"
573+
],
574+
"Swift statements test file": [
575+
"swift/ql/test/extractor-tests/statements/statements.swift",
576+
"swift/ql/test/library-tests/parent/statements.swift"
577+
],
578+
"Swift expressions test file": [
579+
"swift/ql/test/extractor-tests/expressions/expressions.swift",
580+
"swift/ql/test/library-tests/parent/expressions.swift"
581+
],
582+
"Swift patterns test file": [
583+
"swift/ql/test/extractor-tests/patterns/patterns.swift",
584+
"swift/ql/test/library-tests/parent/patterns.swift"
569585
]
570-
}
586+
}

swift/codegen/generators/qlgen.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property):
1919
type=prop.type,
2020
tablename=inflection.tableize(cls.name),
2121
tableparams=["this"] + ["result" if p is prop else "_" for p in cls.properties if p.is_single],
22+
is_child=prop.is_child,
2223
)
2324
elif prop.is_repeated:
2425
return ql.Property(
@@ -28,6 +29,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property):
2829
tablename=inflection.tableize(f"{cls.name}_{prop.name}"),
2930
tableparams=["this", "index", "result"],
3031
is_optional=prop.is_optional,
32+
is_child=prop.is_child,
3133
)
3234
elif prop.is_optional:
3335
return ql.Property(
@@ -36,6 +38,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property):
3638
tablename=inflection.tableize(f"{cls.name}_{prop.name}"),
3739
tableparams=["this", "result"],
3840
is_optional=True,
41+
is_child=prop.is_child,
3942
)
4043
elif prop.is_predicate:
4144
return ql.Property(
@@ -96,6 +99,7 @@ def generate(opts, renderer):
9699
data = schema.load(input)
97100

98101
classes = [get_ql_class(cls) for cls in data.classes]
102+
classes.sort(key=lambda cls: cls.name)
99103
imports = {}
100104

101105
for c in classes:
@@ -110,11 +114,14 @@ def generate(opts, renderer):
110114
stub = ql.Stub(name=c.name, base_import=get_import(qll, opts.swift_dir))
111115
renderer.render(stub, stub_file)
112116

113-
# for example path/to/syntax/generated -> path/to/syntax.qll
117+
# for example path/to/elements -> path/to/elements.qll
114118
include_file = stub_out.with_suffix(".qll")
115-
all_imports = ql.ImportList([v for _, v in sorted(imports.items())])
119+
all_imports = ql.ImportList(list(sorted(imports.values())))
116120
renderer.render(all_imports, include_file)
117121

122+
renderer.render(ql.GetParentImplementation(classes=classes, imports=[get_import(include_file, opts.swift_dir)]),
123+
out / 'GetImmediateParent.qll')
124+
118125
renderer.cleanup(existing)
119126
if opts.ql_format:
120127
format(opts.codeql_binary, renderer.written)

swift/codegen/lib/ql.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Property:
2222
local_var: str = "x"
2323
is_optional: bool = False
2424
is_predicate: bool = False
25+
is_child: bool = False
2526

2627
def __post_init__(self):
2728
if self.tableparams:
@@ -91,3 +92,11 @@ class ImportList:
9192
template: ClassVar = 'ql_imports'
9293

9394
imports: List[str] = field(default_factory=list)
95+
96+
97+
@dataclass
98+
class GetParentImplementation:
99+
template: ClassVar = 'ql_parent'
100+
101+
classes: List[Class] = field(default_factory=list)
102+
imports: List[str] = field(default_factory=list)

swift/codegen/lib/schema.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Property:
1919

2020
name: str
2121
type: str = None
22+
is_child: bool = False
2223

2324

2425
@dataclass
@@ -62,17 +63,18 @@ class Schema:
6263
includes: Set[str] = field(default_factory=set)
6364

6465

65-
def _parse_property(name, type):
66+
def _parse_property(name: str, type: str, is_child: bool = False):
67+
assert not (is_child and type[0].islower()), f"children must have class type, got {type} for {name}"
6668
if type.endswith("?*"):
67-
return RepeatedOptionalProperty(name, type[:-2])
69+
return RepeatedOptionalProperty(name, type[:-2], is_child=is_child)
6870
elif type.endswith("*"):
69-
return RepeatedProperty(name, type[:-1])
71+
return RepeatedProperty(name, type[:-1], is_child=is_child)
7072
elif type.endswith("?"):
71-
return OptionalProperty(name, type[:-1])
73+
return OptionalProperty(name, type[:-1], is_child=is_child)
7274
elif type == "predicate":
7375
return PredicateProperty(name)
7476
else:
75-
return SingleProperty(name, type)
77+
return SingleProperty(name, type, is_child=is_child)
7678

7779

7880
class _DirSelector:
@@ -109,6 +111,8 @@ def load(path):
109111
classes[base].derived.add(name)
110112
elif k == "_dir":
111113
cls.dir = pathlib.Path(v)
114+
elif k == "_children":
115+
cls.properties.extend(_parse_property(kk, vv, is_child=True) for kk, vv in v.items())
112116
if not cls.bases and cls.name != root_class_name:
113117
cls.bases.add(root_class_name)
114118
classes[root_class_name].derived.add(name)

0 commit comments

Comments
 (0)