mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Swift: move toposort in schema.py
This makes the result of code generation independent of the order in which classes are defined in the schema, and makes additional topological sorting not required. Being independent from schema order will be important for reviewing the move to a pure python schema, as generated code will be left untouched.
This commit is contained in:
@@ -16,7 +16,6 @@ import pathlib
|
||||
from typing import Dict
|
||||
|
||||
import inflection
|
||||
from toposort import toposort_flatten
|
||||
|
||||
from swift.codegen.lib import cpp, schema
|
||||
|
||||
@@ -71,13 +70,9 @@ class Processor:
|
||||
)
|
||||
|
||||
def get_classes(self):
|
||||
grouped = {pathlib.Path(): {}}
|
||||
ret = {pathlib.Path(): []}
|
||||
for k, cls in self._classmap.items():
|
||||
grouped.setdefault(cls.dir, {}).update({k: cls})
|
||||
ret = {}
|
||||
for dir, map in grouped.items():
|
||||
inheritance_graph = {k: {b for b in cls.bases if b in map} for k, cls in map.items()}
|
||||
ret[dir] = [self._get_class(cls) for cls in toposort_flatten(inheritance_graph)]
|
||||
ret.setdefault(cls.dir, []).append(self._get_class(cls.name))
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import typing
|
||||
import itertools
|
||||
|
||||
import inflection
|
||||
from toposort import toposort_flatten
|
||||
|
||||
from swift.codegen.lib import schema, ql
|
||||
|
||||
@@ -263,9 +262,7 @@ def generate(opts, renderer):
|
||||
|
||||
imports = {}
|
||||
|
||||
inheritance_graph = {name: cls.bases for name, cls in data.classes.items()}
|
||||
toposorted_names = toposort_flatten(inheritance_graph)
|
||||
db_classes = [classes[name] for name in toposorted_names if not classes[name].ipa]
|
||||
db_classes = [cls for cls in classes.values() if not cls.ipa]
|
||||
renderer.render(ql.DbClasses(db_classes), out / "Raw.qll")
|
||||
|
||||
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
|
||||
@@ -286,8 +283,7 @@ def generate(opts, renderer):
|
||||
include_file = stub_out.with_suffix(".qll")
|
||||
renderer.render(ql.ImportList(list(imports.values())), include_file)
|
||||
|
||||
toposorted_classes = [classes[name] for name in toposorted_names]
|
||||
renderer.render(ql.GetParentImplementation(toposorted_classes), out / 'ParentChild.qll')
|
||||
renderer.render(ql.GetParentImplementation(list(classes.values())), out / 'ParentChild.qll')
|
||||
|
||||
for c in data.classes.values():
|
||||
if _should_skip_qltest(c, data.classes):
|
||||
|
||||
@@ -4,6 +4,7 @@ import pathlib
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Set, Union, Dict, ClassVar, Optional
|
||||
from toposort import toposort_flatten
|
||||
|
||||
import yaml
|
||||
|
||||
@@ -167,4 +168,17 @@ def load(path):
|
||||
cls.bases = [root_class_name]
|
||||
classes[root_class_name].derived.add(name)
|
||||
|
||||
return Schema(classes=classes, includes=set(data.get("_includes", [])))
|
||||
groups = {}
|
||||
|
||||
for name, cls in classes.items():
|
||||
groups.setdefault(cls.dir, []).append(name)
|
||||
|
||||
sorted_classes = {}
|
||||
|
||||
for dir in sorted(groups):
|
||||
group = groups[dir]
|
||||
inheritance = {name: classes[name].bases for name in group}
|
||||
for name in toposort_flatten(inheritance):
|
||||
sorted_classes[name] = classes[name]
|
||||
|
||||
return Schema(classes=sorted_classes, includes=set(data.get("_includes", [])))
|
||||
|
||||
@@ -56,23 +56,6 @@ def test_two_class_hierarchy(generate):
|
||||
]
|
||||
|
||||
|
||||
def test_complex_hierarchy_topologically_ordered(generate):
|
||||
a = cpp.Class(name="A")
|
||||
b = cpp.Class(name="B")
|
||||
c = cpp.Class(name="C", bases=[a])
|
||||
d = cpp.Class(name="D", bases=[a])
|
||||
e = cpp.Class(name="E", bases=[b, c, d], final=True, trap_name="Es")
|
||||
f = cpp.Class(name="F", bases=[c], final=True, trap_name="Fs")
|
||||
assert generate([
|
||||
schema.Class(name="F", bases=["C"]),
|
||||
schema.Class(name="B", derived={"E"}),
|
||||
schema.Class(name="D", bases=["A"], derived={"E"}),
|
||||
schema.Class(name="C", bases=["A"], derived={"E", "F"}),
|
||||
schema.Class(name="E", bases=["B", "C", "D"]),
|
||||
schema.Class(name="A", derived={"C", "D"}),
|
||||
]) == [a, b, c, d, e, f]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("type,expected", [
|
||||
("a", "a"),
|
||||
("string", "std::string"),
|
||||
@@ -154,8 +137,8 @@ def test_classes_with_dirs(generate_grouped):
|
||||
assert generate_grouped([
|
||||
schema.Class(name="A"),
|
||||
schema.Class(name="B", dir=pathlib.Path("foo")),
|
||||
schema.Class(name="C", bases=["CBase"], dir=pathlib.Path("bar")),
|
||||
schema.Class(name="CBase", derived={"C"}, dir=pathlib.Path("bar")),
|
||||
schema.Class(name="C", bases=["CBase"], dir=pathlib.Path("bar")),
|
||||
schema.Class(name="D", dir=pathlib.Path("foo/bar/baz")),
|
||||
]) == {
|
||||
".": [cpp.Class(name="A", trap_name="As", final=True)],
|
||||
|
||||
@@ -177,10 +177,10 @@ def test_hierarchy_imports(generate_import_list):
|
||||
|
||||
def test_hierarchy_children(generate_children_implementations):
|
||||
assert generate_children_implementations([
|
||||
schema.Class("D", bases=["B", "C"]),
|
||||
schema.Class("C", bases=["A"], derived={"D"}),
|
||||
schema.Class("B", bases=["A"], derived={"D"}),
|
||||
schema.Class("A", derived={"B", "C"}),
|
||||
schema.Class("B", bases=["A"], derived={"D"}),
|
||||
schema.Class("C", bases=["A"], derived={"D"}),
|
||||
schema.Class("D", bases=["B", "C"]),
|
||||
]) == ql.GetParentImplementation(
|
||||
classes=[ql.Class(name="A"),
|
||||
ql.Class(name="B", bases=["A"], imports=[
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user