зеркало из https://github.com/github/codeql.git
Merge pull request #11292 from github/redsun82/swift-remove-ipa-from-dbscheme-cpp
Swift: remove synthesized classes from the dbscheme
This commit is contained in:
Коммит
2796c46598
|
@ -80,16 +80,10 @@ class Processor:
|
|||
trap_name=trap_name,
|
||||
)
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _is_ipa(self, name: str) -> bool:
|
||||
cls = self._classmap[name]
|
||||
return cls.ipa is not None or (
|
||||
cls.derived and all(self._is_ipa(d) for d in cls.derived))
|
||||
|
||||
def get_classes(self):
|
||||
ret = {'': []}
|
||||
for k, cls in self._classmap.items():
|
||||
if not self._is_ipa(k):
|
||||
if not cls.ipa:
|
||||
ret.setdefault(cls.group, []).append(self._get_class(cls.name))
|
||||
return ret
|
||||
|
||||
|
|
|
@ -38,10 +38,12 @@ def dbtype(typename: str, add_or_none_except: typing.Optional[str] = None) -> st
|
|||
return typename
|
||||
|
||||
|
||||
def cls_to_dbscheme(cls: schema.Class, add_or_none_except: typing.Optional[str] = None):
|
||||
def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], add_or_none_except: typing.Optional[str] = None):
|
||||
""" Yield all dbscheme entities needed to model class `cls` """
|
||||
if cls.ipa:
|
||||
return
|
||||
if cls.derived:
|
||||
yield Union(dbtype(cls.name), (dbtype(c) for c in cls.derived))
|
||||
yield Union(dbtype(cls.name), (dbtype(c) for c in cls.derived if not lookup[c].ipa))
|
||||
dir = pathlib.Path(cls.group) if cls.group else None
|
||||
# output a table specific to a class only if it is a leaf class or it has 1-to-1 properties
|
||||
# Leaf classes need a table to bind the `@` ids
|
||||
|
@ -96,7 +98,7 @@ def cls_to_dbscheme(cls: schema.Class, add_or_none_except: typing.Optional[str]
|
|||
|
||||
def get_declarations(data: schema.Schema):
|
||||
add_or_none_except = data.root_class.name if data.null else None
|
||||
declarations = [d for cls in data.classes.values() for d in cls_to_dbscheme(cls, add_or_none_except)]
|
||||
declarations = [d for cls in data.classes.values() for d in cls_to_dbscheme(cls, data.classes, add_or_none_except)]
|
||||
if data.null:
|
||||
property_classes = {
|
||||
prop.type for cls in data.classes.values() for prop in cls.properties
|
||||
|
|
|
@ -86,7 +86,8 @@ class Class:
|
|||
properties: List[Property] = field(default_factory=list)
|
||||
group: str = ""
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
ipa: Optional[IpaInfo] = None
|
||||
ipa: Optional[Union[IpaInfo, bool]] = None
|
||||
"""^^^ filled with `True` for non-final classes with only synthesized final descendants """
|
||||
doc: List[str] = field(default_factory=list)
|
||||
default_doc_name: Optional[str] = None
|
||||
|
||||
|
@ -247,6 +248,36 @@ def _toposort_classes_by_group(classes: typing.Dict[str, Class]) -> typing.Dict[
|
|||
return ret
|
||||
|
||||
|
||||
def _fill_ipa_information(classes: typing.Dict[str, Class]):
|
||||
""" Take a dictionary where the `ipa` field is filled for all explicitly synthesized classes
|
||||
and update it so that all non-final classes that have only synthesized final descendants
|
||||
get `True` as` value for the `ipa` field
|
||||
"""
|
||||
if not classes:
|
||||
return
|
||||
|
||||
is_ipa: typing.Dict[str, bool] = {}
|
||||
|
||||
def fill_is_ipa(name: str):
|
||||
if name not in is_ipa:
|
||||
cls = classes[name]
|
||||
for d in cls.derived:
|
||||
fill_is_ipa(d)
|
||||
if cls.ipa is not None:
|
||||
is_ipa[name] = True
|
||||
elif not cls.derived:
|
||||
is_ipa[name] = False
|
||||
else:
|
||||
is_ipa[name] = all(is_ipa[d] for d in cls.derived)
|
||||
|
||||
root = next(iter(classes))
|
||||
fill_is_ipa(root)
|
||||
|
||||
for name, cls in classes.items():
|
||||
if cls.ipa is None and is_ipa[name]:
|
||||
cls.ipa = True
|
||||
|
||||
|
||||
def load(m: types.ModuleType) -> Schema:
|
||||
includes = set()
|
||||
classes = {}
|
||||
|
@ -274,6 +305,8 @@ def load(m: types.ModuleType) -> Schema:
|
|||
null = name
|
||||
cls.is_null_class = True
|
||||
|
||||
_fill_ipa_information(classes)
|
||||
|
||||
return Schema(includes=includes, classes=_toposort_classes_by_group(classes), null=null)
|
||||
|
||||
|
||||
|
|
|
@ -182,6 +182,10 @@ def test_cpp_skip_pragma(generate):
|
|||
|
||||
def test_ipa_classes_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(
|
||||
name="W",
|
||||
ipa=schema.IpaInfo(),
|
||||
),
|
||||
schema.Class(
|
||||
name="X",
|
||||
ipa=schema.IpaInfo(from_class="A"),
|
||||
|
@ -198,66 +202,5 @@ def test_ipa_classes_ignored(generate):
|
|||
]
|
||||
|
||||
|
||||
def test_ipa_hierarchy_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(
|
||||
name="Root",
|
||||
derived={"Base", "Z"},
|
||||
),
|
||||
schema.Class(
|
||||
name="Base",
|
||||
bases=["Root"],
|
||||
derived={"X", "Y"}
|
||||
),
|
||||
schema.Class(
|
||||
name="X",
|
||||
bases=["Base"],
|
||||
ipa=schema.IpaInfo(from_class="A"),
|
||||
),
|
||||
schema.Class(
|
||||
name="Y",
|
||||
bases=["Base"],
|
||||
ipa=schema.IpaInfo(on_arguments={"a": "A", "b": "int"}),
|
||||
),
|
||||
schema.Class(
|
||||
name="Z",
|
||||
ipa=schema.IpaInfo(from_class="A"),
|
||||
),
|
||||
]) == []
|
||||
|
||||
|
||||
def test_ipa_hierarchy_not_ignored_with_non_ipa_descendant(generate):
|
||||
root = cpp.Class(name="Root")
|
||||
base = cpp.Class(name="Base", bases=[root])
|
||||
assert generate([
|
||||
schema.Class(
|
||||
name="Root",
|
||||
derived={"Base", "Z"},
|
||||
),
|
||||
schema.Class(
|
||||
name="Base",
|
||||
bases=["Root"],
|
||||
derived={"X", "Y"}
|
||||
),
|
||||
schema.Class(
|
||||
name="X",
|
||||
bases=["Base"],
|
||||
),
|
||||
schema.Class(
|
||||
name="Y",
|
||||
bases=["Base"],
|
||||
ipa=schema.IpaInfo(on_arguments={"a": "A", "b": "int"}),
|
||||
),
|
||||
schema.Class(
|
||||
name="Z",
|
||||
ipa=schema.IpaInfo(from_class="A"),
|
||||
),
|
||||
]) == [
|
||||
root,
|
||||
base,
|
||||
cpp.Class(name="X", bases=[base], final=True, trap_name="Xes"),
|
||||
]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
|
|
|
@ -255,8 +255,9 @@ def test_final_class_with_more_fields(generate, dir_param):
|
|||
|
||||
def test_empty_class_with_derived(generate):
|
||||
assert generate([
|
||||
schema.Class(
|
||||
name="Base", derived={"Left", "Right"}),
|
||||
schema.Class(name="Base", derived={"Left", "Right"}),
|
||||
schema.Class(name="Left", bases=["Base"]),
|
||||
schema.Class(name="Right", bases=["Base"]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file,
|
||||
includes=[],
|
||||
|
@ -265,6 +266,14 @@ def test_empty_class_with_derived(generate):
|
|||
lhs="@base",
|
||||
rhs=["@left", "@right"],
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="lefts",
|
||||
columns=[dbscheme.Column("id", "@left", binding=True)],
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="rights",
|
||||
columns=[dbscheme.Column("id", "@right", binding=True)],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -278,6 +287,8 @@ def test_class_with_derived_and_single_property(generate, dir_param):
|
|||
properties=[
|
||||
schema.SingleProperty("single", "Prop"),
|
||||
]),
|
||||
schema.Class(name="Left", bases=["Base"]),
|
||||
schema.Class(name="Right", bases=["Base"]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file,
|
||||
includes=[],
|
||||
|
@ -294,7 +305,15 @@ def test_class_with_derived_and_single_property(generate, dir_param):
|
|||
dbscheme.Column('single', '@prop'),
|
||||
],
|
||||
dir=dir_param.expected,
|
||||
)
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="lefts",
|
||||
columns=[dbscheme.Column("id", "@left", binding=True)],
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="rights",
|
||||
columns=[dbscheme.Column("id", "@right", binding=True)],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -308,6 +327,8 @@ def test_class_with_derived_and_optional_property(generate, dir_param):
|
|||
properties=[
|
||||
schema.OptionalProperty("opt", "Prop"),
|
||||
]),
|
||||
schema.Class(name="Left", bases=["Base"]),
|
||||
schema.Class(name="Right", bases=["Base"]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file,
|
||||
includes=[],
|
||||
|
@ -324,7 +345,15 @@ def test_class_with_derived_and_optional_property(generate, dir_param):
|
|||
dbscheme.Column('opt', '@prop'),
|
||||
],
|
||||
dir=dir_param.expected,
|
||||
)
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="lefts",
|
||||
columns=[dbscheme.Column("id", "@left", binding=True)],
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="rights",
|
||||
columns=[dbscheme.Column("id", "@right", binding=True)],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -338,6 +367,8 @@ def test_class_with_derived_and_repeated_property(generate, dir_param):
|
|||
properties=[
|
||||
schema.RepeatedProperty("rep", "Prop"),
|
||||
]),
|
||||
schema.Class(name="Left", bases=["Base"]),
|
||||
schema.Class(name="Right", bases=["Base"]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file,
|
||||
includes=[],
|
||||
|
@ -355,7 +386,15 @@ def test_class_with_derived_and_repeated_property(generate, dir_param):
|
|||
dbscheme.Column('rep', '@prop'),
|
||||
],
|
||||
dir=dir_param.expected,
|
||||
)
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="lefts",
|
||||
columns=[dbscheme.Column("id", "@left", binding=True)],
|
||||
),
|
||||
dbscheme.Table(
|
||||
name="rights",
|
||||
columns=[dbscheme.Column("id", "@right", binding=True)],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -469,5 +508,37 @@ def test_null_class(generate):
|
|||
)
|
||||
|
||||
|
||||
def test_ipa_classes_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(name="A", ipa=schema.IpaInfo()),
|
||||
schema.Class(name="B", ipa=schema.IpaInfo(from_class="A")),
|
||||
schema.Class(name="C", ipa=schema.IpaInfo(on_arguments={"x": "A"})),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file,
|
||||
includes=[],
|
||||
declarations=[],
|
||||
)
|
||||
|
||||
|
||||
def test_ipa_derived_classes_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(name="A", derived={"B", "C"}),
|
||||
schema.Class(name="B", bases=["A"], ipa=schema.IpaInfo()),
|
||||
schema.Class(name="C", bases=["A"]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file,
|
||||
includes=[],
|
||||
declarations=[
|
||||
dbscheme.Union("@a", ["@c"]),
|
||||
dbscheme.Table(
|
||||
name="cs",
|
||||
columns=[
|
||||
dbscheme.Column("id", "@c", binding=True),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
|
|
|
@ -340,7 +340,7 @@ def test_ipa_from_class():
|
|||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}),
|
||||
'A': schema.Class('A', derived={'B'}, ipa=True),
|
||||
'B': schema.Class('B', bases=['A'], ipa=schema.IpaInfo(from_class="A")),
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@ def test_ipa_class_on():
|
|||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}),
|
||||
'A': schema.Class('A', derived={'B'}, ipa=True),
|
||||
'B': schema.Class('B', bases=['A'], ipa=schema.IpaInfo(on_arguments={'a': 'A', 'i': 'int'})),
|
||||
}
|
||||
|
||||
|
@ -414,6 +414,39 @@ def test_ipa_class_on_dangling():
|
|||
pass
|
||||
|
||||
|
||||
def test_ipa_class_hierarchy():
|
||||
@schema.load
|
||||
class data:
|
||||
class Root:
|
||||
pass
|
||||
|
||||
class Base(Root):
|
||||
pass
|
||||
|
||||
class Intermediate(Base):
|
||||
pass
|
||||
|
||||
@defs.synth.on_arguments(a=Base, i=defs.int)
|
||||
class A(Intermediate):
|
||||
pass
|
||||
|
||||
@defs.synth.from_class(Base)
|
||||
class B(Base):
|
||||
pass
|
||||
|
||||
class C(Root):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'Root': schema.Class('Root', derived={'Base', 'C'}),
|
||||
'Base': schema.Class('Base', bases=['Root'], derived={'Intermediate', 'B'}, ipa=True),
|
||||
'Intermediate': schema.Class('Intermediate', bases=['Base'], derived={'A'}, ipa=True),
|
||||
'A': schema.Class('A', bases=['Intermediate'], ipa=schema.IpaInfo(on_arguments={'a': 'Base', 'i': 'int'})),
|
||||
'B': schema.Class('B', bases=['Base'], ipa=schema.IpaInfo(from_class='Base')),
|
||||
'C': schema.Class('C', bases=['Root']),
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring():
|
||||
@schema.load
|
||||
class data:
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
|||
description: Remove unfilled tables that were generated for actually synthesized QL types
|
||||
compatibility: full
|
|
@ -52,7 +52,6 @@ callable_bodies(
|
|||
|
||||
@file =
|
||||
@db_file
|
||||
| @unknown_file
|
||||
;
|
||||
|
||||
#keyset[id]
|
||||
|
@ -77,7 +76,6 @@ locatable_locations(
|
|||
|
||||
@location =
|
||||
@db_location
|
||||
| @unknown_location
|
||||
;
|
||||
|
||||
#keyset[id]
|
||||
|
@ -135,14 +133,6 @@ diagnostics(
|
|||
| @unspecified_element
|
||||
;
|
||||
|
||||
unknown_files(
|
||||
unique int id: @unknown_file
|
||||
);
|
||||
|
||||
unknown_locations(
|
||||
unique int id: @unknown_location
|
||||
);
|
||||
|
||||
unspecified_elements(
|
||||
unique int id: @unspecified_element,
|
||||
string property: string ref,
|
||||
|
@ -941,7 +931,6 @@ lazy_initializer_exprs( //dir=expr
|
|||
@lookup_expr =
|
||||
@dynamic_lookup_expr
|
||||
| @member_ref_expr
|
||||
| @method_ref_expr
|
||||
| @subscript_expr
|
||||
;
|
||||
|
||||
|
@ -1334,10 +1323,6 @@ metatype_conversion_exprs( //dir=expr
|
|||
unique int id: @metatype_conversion_expr
|
||||
);
|
||||
|
||||
method_ref_exprs( //dir=expr
|
||||
unique int id: @method_ref_expr
|
||||
);
|
||||
|
||||
nil_literal_exprs( //dir=expr
|
||||
unique int id: @nil_literal_expr
|
||||
);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,5 @@
|
|||
description: Remove unfilled tables that were generated for actually synthesized QL types
|
||||
compatibility: full
|
||||
unknown_files.rel: delete
|
||||
unknown_locations: delete
|
||||
method_ref_exprs: delete
|
Загрузка…
Ссылка в новой задаче