mirror of
https://github.com/github/codeql.git
synced 2026-04-23 07:45:17 +02:00
Merge pull request #13258 from github/redsun82/swift-synth-properties
Codegen: allow `synth` properties of non-`synth` classes
This commit is contained in:
@@ -76,7 +76,7 @@ class Processor:
|
||||
bases=[self._get_class(b) for b in cls.bases],
|
||||
fields=[
|
||||
_get_field(cls, p, self._add_or_none_except)
|
||||
for p in cls.properties if "cpp_skip" not in p.pragmas
|
||||
for p in cls.properties if "cpp_skip" not in p.pragmas and not p.synth
|
||||
],
|
||||
final=not cls.derived,
|
||||
trap_name=trap_name,
|
||||
@@ -85,7 +85,7 @@ class Processor:
|
||||
def get_classes(self):
|
||||
ret = {'': []}
|
||||
for k, cls in self._classmap.items():
|
||||
if not cls.ipa:
|
||||
if not cls.synth:
|
||||
ret.setdefault(cls.group, []).append(self._get_class(cls.name))
|
||||
return ret
|
||||
|
||||
|
||||
@@ -40,16 +40,16 @@ def dbtype(typename: str, add_or_none_except: typing.Optional[str] = None) -> st
|
||||
|
||||
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:
|
||||
if cls.synth:
|
||||
return
|
||||
if cls.derived:
|
||||
yield Union(dbtype(cls.name), (dbtype(c) for c in cls.derived if not lookup[c].ipa))
|
||||
yield Union(dbtype(cls.name), (dbtype(c) for c in cls.derived if not lookup[c].synth))
|
||||
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
|
||||
# 1-to-1 properties are added to a class specific table
|
||||
# in other cases, separate tables are used for the properties, and a class specific table is unneeded
|
||||
if not cls.derived or any(f.is_single for f in cls.properties):
|
||||
if not cls.derived or any(f.is_single and not f.synth for f in cls.properties):
|
||||
binding = not cls.derived
|
||||
keyset = KeySet(["id"]) if cls.derived else None
|
||||
yield Table(
|
||||
@@ -58,12 +58,15 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
|
||||
columns=[
|
||||
Column("id", type=dbtype(cls.name), binding=binding),
|
||||
] + [
|
||||
Column(f.name, dbtype(f.type, add_or_none_except)) for f in cls.properties if f.is_single
|
||||
Column(f.name, dbtype(f.type, add_or_none_except))
|
||||
for f in cls.properties if f.is_single and not f.synth
|
||||
],
|
||||
dir=dir,
|
||||
)
|
||||
# use property-specific tables for 1-to-many and 1-to-at-most-1 properties
|
||||
for f in cls.properties:
|
||||
if f.synth:
|
||||
continue
|
||||
if f.is_unordered:
|
||||
yield Table(
|
||||
name=inflection.tableize(f"{cls.name}_{f.name}"),
|
||||
|
||||
@@ -111,6 +111,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
|
||||
is_predicate=prop.is_predicate,
|
||||
is_unordered=prop.is_unordered,
|
||||
description=prop.description,
|
||||
synth=bool(cls.synth) or prop.synth,
|
||||
type_is_hideable=lookup[prop.type].hideable if prop.type in lookup else False,
|
||||
)
|
||||
if prop.is_single:
|
||||
@@ -163,7 +164,6 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
|
||||
final=not cls.derived,
|
||||
properties=properties,
|
||||
dir=pathlib.Path(cls.group or ""),
|
||||
ipa=bool(cls.ipa),
|
||||
doc=cls.doc,
|
||||
hideable=cls.hideable,
|
||||
**pragmas,
|
||||
@@ -179,26 +179,26 @@ def _to_db_type(x: str) -> str:
|
||||
_final_db_class_lookup = {}
|
||||
|
||||
|
||||
def get_ql_ipa_class_db(name: str) -> ql.Synth.FinalClassDb:
|
||||
def get_ql_synth_class_db(name: str) -> ql.Synth.FinalClassDb:
|
||||
return _final_db_class_lookup.setdefault(name, ql.Synth.FinalClassDb(name=name,
|
||||
params=[
|
||||
ql.Synth.Param("id", _to_db_type(name))]))
|
||||
|
||||
|
||||
def get_ql_ipa_class(cls: schema.Class):
|
||||
def get_ql_synth_class(cls: schema.Class):
|
||||
if cls.derived:
|
||||
return ql.Synth.NonFinalClass(name=cls.name, derived=sorted(cls.derived),
|
||||
root=not cls.bases)
|
||||
if cls.ipa and cls.ipa.from_class is not None:
|
||||
source = cls.ipa.from_class
|
||||
get_ql_ipa_class_db(source).subtract_type(cls.name)
|
||||
return ql.Synth.FinalClassDerivedIpa(name=cls.name,
|
||||
params=[ql.Synth.Param("id", _to_db_type(source))])
|
||||
if cls.ipa and cls.ipa.on_arguments is not None:
|
||||
return ql.Synth.FinalClassFreshIpa(name=cls.name,
|
||||
params=[ql.Synth.Param(k, _to_db_type(v))
|
||||
for k, v in cls.ipa.on_arguments.items()])
|
||||
return get_ql_ipa_class_db(cls.name)
|
||||
if cls.synth and cls.synth.from_class is not None:
|
||||
source = cls.synth.from_class
|
||||
get_ql_synth_class_db(source).subtract_type(cls.name)
|
||||
return ql.Synth.FinalClassDerivedSynth(name=cls.name,
|
||||
params=[ql.Synth.Param("id", _to_db_type(source))])
|
||||
if cls.synth and cls.synth.on_arguments is not None:
|
||||
return ql.Synth.FinalClassFreshSynth(name=cls.name,
|
||||
params=[ql.Synth.Param(k, _to_db_type(v))
|
||||
for k, v in cls.synth.on_arguments.items()])
|
||||
return get_ql_synth_class_db(cls.name)
|
||||
|
||||
|
||||
def get_import(file: pathlib.Path, root_dir: pathlib.Path):
|
||||
@@ -291,26 +291,26 @@ def _should_skip_qltest(cls: schema.Class, lookup: typing.Dict[str, schema.Class
|
||||
|
||||
|
||||
def _get_stub(cls: schema.Class, base_import: str, generated_import_prefix: str) -> ql.Stub:
|
||||
if isinstance(cls.ipa, schema.IpaInfo):
|
||||
if cls.ipa.from_class is not None:
|
||||
if isinstance(cls.synth, schema.SynthInfo):
|
||||
if cls.synth.from_class is not None:
|
||||
accessors = [
|
||||
ql.IpaUnderlyingAccessor(
|
||||
ql.SynthUnderlyingAccessor(
|
||||
argument="Entity",
|
||||
type=_to_db_type(cls.ipa.from_class),
|
||||
type=_to_db_type(cls.synth.from_class),
|
||||
constructorparams=["result"]
|
||||
)
|
||||
]
|
||||
elif cls.ipa.on_arguments is not None:
|
||||
elif cls.synth.on_arguments is not None:
|
||||
accessors = [
|
||||
ql.IpaUnderlyingAccessor(
|
||||
ql.SynthUnderlyingAccessor(
|
||||
argument=inflection.camelize(arg),
|
||||
type=_to_db_type(type),
|
||||
constructorparams=["result" if a == arg else "_" for a in cls.ipa.on_arguments]
|
||||
) for arg, type in cls.ipa.on_arguments.items()
|
||||
constructorparams=["result" if a == arg else "_" for a in cls.synth.on_arguments]
|
||||
) for arg, type in cls.synth.on_arguments.items()
|
||||
]
|
||||
else:
|
||||
accessors = []
|
||||
return ql.Stub(name=cls.name, base_import=base_import, import_prefix=generated_import_prefix, ipa_accessors=accessors)
|
||||
return ql.Stub(name=cls.name, base_import=base_import, import_prefix=generated_import_prefix, synth_accessors=accessors)
|
||||
|
||||
|
||||
def generate(opts, renderer):
|
||||
@@ -344,7 +344,7 @@ def generate(opts, renderer):
|
||||
with renderer.manage(generated=generated, stubs=stubs, registry=opts.generated_registry,
|
||||
force=opts.force) as renderer:
|
||||
|
||||
db_classes = [cls for cls in classes.values() if not cls.ipa]
|
||||
db_classes = [cls for name, cls in classes.items() if not data.classes[name].synth]
|
||||
renderer.render(ql.DbClasses(db_classes), out / "Raw.qll")
|
||||
|
||||
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
|
||||
@@ -401,32 +401,32 @@ def generate(opts, renderer):
|
||||
elements_module=elements_module,
|
||||
property=p), test_dir / f"{c.name}_{p.getter}.ql")
|
||||
|
||||
final_ipa_types = []
|
||||
non_final_ipa_types = []
|
||||
final_synth_types = []
|
||||
non_final_synth_types = []
|
||||
constructor_imports = []
|
||||
ipa_constructor_imports = []
|
||||
synth_constructor_imports = []
|
||||
stubs = {}
|
||||
for cls in sorted(data.classes.values(), key=lambda cls: (cls.group, cls.name)):
|
||||
ipa_type = get_ql_ipa_class(cls)
|
||||
if ipa_type.is_final:
|
||||
final_ipa_types.append(ipa_type)
|
||||
if ipa_type.has_params:
|
||||
synth_type = get_ql_synth_class(cls)
|
||||
if synth_type.is_final:
|
||||
final_synth_types.append(synth_type)
|
||||
if synth_type.has_params:
|
||||
stub_file = stub_out / cls.group / f"{cls.name}Constructor.qll"
|
||||
if not renderer.is_customized_stub(stub_file):
|
||||
# stub rendering must be postponed as we might not have yet all subtracted ipa types in `ipa_type`
|
||||
stubs[stub_file] = ql.Synth.ConstructorStub(ipa_type, import_prefix=generated_import_prefix)
|
||||
# stub rendering must be postponed as we might not have yet all subtracted synth types in `synth_type`
|
||||
stubs[stub_file] = ql.Synth.ConstructorStub(synth_type, import_prefix=generated_import_prefix)
|
||||
constructor_import = get_import(stub_file, opts.root_dir)
|
||||
constructor_imports.append(constructor_import)
|
||||
if ipa_type.is_ipa:
|
||||
ipa_constructor_imports.append(constructor_import)
|
||||
if synth_type.is_synth:
|
||||
synth_constructor_imports.append(constructor_import)
|
||||
else:
|
||||
non_final_ipa_types.append(ipa_type)
|
||||
non_final_synth_types.append(synth_type)
|
||||
|
||||
for stub_file, data in stubs.items():
|
||||
renderer.render(data, stub_file)
|
||||
renderer.render(ql.Synth.Types(root.name, generated_import_prefix,
|
||||
final_ipa_types, non_final_ipa_types), out / "Synth.qll")
|
||||
final_synth_types, non_final_synth_types), out / "Synth.qll")
|
||||
renderer.render(ql.ImportList(constructor_imports), out / "SynthConstructors.qll")
|
||||
renderer.render(ql.ImportList(ipa_constructor_imports), out / "PureSynthConstructors.qll")
|
||||
renderer.render(ql.ImportList(synth_constructor_imports), out / "PureSynthConstructors.qll")
|
||||
if opts.ql_format:
|
||||
format(opts.codeql_binary, renderer.written)
|
||||
|
||||
@@ -42,6 +42,7 @@ class Property:
|
||||
description: List[str] = field(default_factory=list)
|
||||
doc: Optional[str] = None
|
||||
doc_plural: Optional[str] = None
|
||||
synth: bool = False
|
||||
type_is_hideable: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
@@ -112,7 +113,6 @@ class Class:
|
||||
qltest_collapse_hierarchy: bool = False
|
||||
qltest_uncollapse_hierarchy: bool = False
|
||||
ql_internal: bool = False
|
||||
ipa: bool = False
|
||||
doc: List[str] = field(default_factory=list)
|
||||
hideable: bool = False
|
||||
|
||||
@@ -147,7 +147,7 @@ class Class:
|
||||
|
||||
|
||||
@dataclass
|
||||
class IpaUnderlyingAccessor:
|
||||
class SynthUnderlyingAccessor:
|
||||
argument: str
|
||||
type: str
|
||||
constructorparams: List[Param]
|
||||
@@ -165,11 +165,11 @@ class Stub:
|
||||
name: str
|
||||
base_import: str
|
||||
import_prefix: str
|
||||
ipa_accessors: List[IpaUnderlyingAccessor] = field(default_factory=list)
|
||||
synth_accessors: List[SynthUnderlyingAccessor] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def has_ipa_accessors(self) -> bool:
|
||||
return bool(self.ipa_accessors)
|
||||
def has_synth_accessors(self) -> bool:
|
||||
return bool(self.synth_accessors)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -245,8 +245,8 @@ class Synth:
|
||||
@dataclass
|
||||
class FinalClass(Class):
|
||||
is_final: ClassVar = True
|
||||
is_derived_ipa: ClassVar = False
|
||||
is_fresh_ipa: ClassVar = False
|
||||
is_derived_synth: ClassVar = False
|
||||
is_fresh_synth: ClassVar = False
|
||||
is_db: ClassVar = False
|
||||
|
||||
params: List["Synth.Param"] = field(default_factory=list)
|
||||
@@ -256,37 +256,37 @@ class Synth:
|
||||
self.params[0].first = True
|
||||
|
||||
@property
|
||||
def is_ipa(self):
|
||||
return self.is_fresh_ipa or self.is_derived_ipa
|
||||
def is_synth(self):
|
||||
return self.is_fresh_synth or self.is_derived_synth
|
||||
|
||||
@property
|
||||
def has_params(self) -> bool:
|
||||
return bool(self.params)
|
||||
|
||||
@dataclass
|
||||
class FinalClassIpa(FinalClass):
|
||||
class FinalClassSynth(FinalClass):
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class FinalClassDerivedIpa(FinalClassIpa):
|
||||
is_derived_ipa: ClassVar = True
|
||||
class FinalClassDerivedSynth(FinalClassSynth):
|
||||
is_derived_synth: ClassVar = True
|
||||
|
||||
@dataclass
|
||||
class FinalClassFreshIpa(FinalClassIpa):
|
||||
is_fresh_ipa: ClassVar = True
|
||||
class FinalClassFreshSynth(FinalClassSynth):
|
||||
is_fresh_synth: ClassVar = True
|
||||
|
||||
@dataclass
|
||||
class FinalClassDb(FinalClass):
|
||||
is_db: ClassVar = True
|
||||
|
||||
subtracted_ipa_types: List["Synth.Class"] = field(default_factory=list)
|
||||
subtracted_synth_types: List["Synth.Class"] = field(default_factory=list)
|
||||
|
||||
def subtract_type(self, type: str):
|
||||
self.subtracted_ipa_types.append(Synth.Class(type, first=not self.subtracted_ipa_types))
|
||||
self.subtracted_synth_types.append(Synth.Class(type, first=not self.subtracted_synth_types))
|
||||
|
||||
@property
|
||||
def has_subtracted_ipa_types(self) -> bool:
|
||||
return bool(self.subtracted_ipa_types)
|
||||
def has_subtracted_synth_types(self) -> bool:
|
||||
return bool(self.subtracted_synth_types)
|
||||
|
||||
@property
|
||||
def db_id(self) -> str:
|
||||
@@ -304,7 +304,7 @@ class Synth:
|
||||
|
||||
@dataclass
|
||||
class Types:
|
||||
template: ClassVar = "ql_ipa_types"
|
||||
template: ClassVar = "ql_synth_types"
|
||||
|
||||
root: str
|
||||
import_prefix: str
|
||||
@@ -317,7 +317,7 @@ class Synth:
|
||||
|
||||
@dataclass
|
||||
class ConstructorStub:
|
||||
template: ClassVar = "ql_ipa_constructor_stub"
|
||||
template: ClassVar = "ql_synth_constructor_stub"
|
||||
|
||||
cls: "Synth.FinalClass"
|
||||
import_prefix: str
|
||||
|
||||
@@ -34,6 +34,7 @@ class Property:
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
doc: Optional[str] = None
|
||||
description: List[str] = field(default_factory=list)
|
||||
synth: bool = False
|
||||
|
||||
@property
|
||||
def is_single(self) -> bool:
|
||||
@@ -74,7 +75,7 @@ RepeatedUnorderedProperty = functools.partial(Property, Property.Kind.REPEATED_U
|
||||
|
||||
|
||||
@dataclass
|
||||
class IpaInfo:
|
||||
class SynthInfo:
|
||||
from_class: Optional[str] = None
|
||||
on_arguments: Optional[Dict[str, str]] = None
|
||||
|
||||
@@ -87,7 +88,7 @@ class Class:
|
||||
properties: List[Property] = field(default_factory=list)
|
||||
group: str = ""
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
ipa: Optional[Union[IpaInfo, bool]] = None
|
||||
synth: Optional[Union[SynthInfo, 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
|
||||
@@ -104,10 +105,10 @@ class Class:
|
||||
_check_type(d, known)
|
||||
for p in self.properties:
|
||||
_check_type(p.type, known)
|
||||
if self.ipa is not None:
|
||||
_check_type(self.ipa.from_class, known)
|
||||
if self.ipa.on_arguments is not None:
|
||||
for t in self.ipa.on_arguments.values():
|
||||
if self.synth is not None:
|
||||
_check_type(self.synth.from_class, known)
|
||||
if self.synth.on_arguments is not None:
|
||||
for t in self.synth.on_arguments.values():
|
||||
_check_type(t, known)
|
||||
|
||||
|
||||
|
||||
@@ -44,10 +44,15 @@ class _Namespace:
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
|
||||
class _SynthModifier(_schema.PropertyModifier, _Namespace):
|
||||
def modify(self, prop: _schema.Property):
|
||||
prop.synth = True
|
||||
|
||||
|
||||
qltest = _Namespace()
|
||||
ql = _Namespace()
|
||||
cpp = _Namespace()
|
||||
synth = _Namespace()
|
||||
synth = _SynthModifier()
|
||||
|
||||
|
||||
@_dataclass
|
||||
@@ -155,7 +160,7 @@ def group(name: str = "") -> _ClassDecorator:
|
||||
return _annotate(group=name)
|
||||
|
||||
|
||||
synth.from_class = lambda ref: _annotate(ipa=_schema.IpaInfo(
|
||||
synth.from_class = lambda ref: _annotate(synth=_schema.SynthInfo(
|
||||
from_class=_schema.get_type_name(ref)))
|
||||
synth.on_arguments = lambda **kwargs: _annotate(
|
||||
ipa=_schema.IpaInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()}))
|
||||
synth=_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()}))
|
||||
|
||||
@@ -40,7 +40,7 @@ def _get_class(cls: type) -> schema.Class:
|
||||
hideable=getattr(cls, "_hideable", False),
|
||||
# in the following we don't use `getattr` to avoid inheriting
|
||||
pragmas=cls.__dict__.get("_pragmas", []),
|
||||
ipa=cls.__dict__.get("_ipa", None),
|
||||
synth=cls.__dict__.get("_synth", None),
|
||||
properties=[
|
||||
a | _PropertyNamer(n)
|
||||
for n, a in cls.__dict__.get("__annotations__", {}).items()
|
||||
@@ -65,34 +65,34 @@ def _toposort_classes_by_group(classes: typing.Dict[str, schema.Class]) -> typin
|
||||
return ret
|
||||
|
||||
|
||||
def _fill_ipa_information(classes: typing.Dict[str, schema.Class]):
|
||||
""" Take a dictionary where the `ipa` field is filled for all explicitly synthesized classes
|
||||
def _fill_synth_information(classes: typing.Dict[str, schema.Class]):
|
||||
""" Take a dictionary where the `synth` 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
|
||||
get `True` as` value for the `synth` field
|
||||
"""
|
||||
if not classes:
|
||||
return
|
||||
|
||||
is_ipa: typing.Dict[str, bool] = {}
|
||||
is_synth: typing.Dict[str, bool] = {}
|
||||
|
||||
def fill_is_ipa(name: str):
|
||||
if name not in is_ipa:
|
||||
def fill_is_synth(name: str):
|
||||
if name not in is_synth:
|
||||
cls = classes[name]
|
||||
for d in cls.derived:
|
||||
fill_is_ipa(d)
|
||||
if cls.ipa is not None:
|
||||
is_ipa[name] = True
|
||||
fill_is_synth(d)
|
||||
if cls.synth is not None:
|
||||
is_synth[name] = True
|
||||
elif not cls.derived:
|
||||
is_ipa[name] = False
|
||||
is_synth[name] = False
|
||||
else:
|
||||
is_ipa[name] = all(is_ipa[d] for d in cls.derived)
|
||||
is_synth[name] = all(is_synth[d] for d in cls.derived)
|
||||
|
||||
root = next(iter(classes))
|
||||
fill_is_ipa(root)
|
||||
fill_is_synth(root)
|
||||
|
||||
for name, cls in classes.items():
|
||||
if cls.ipa is None and is_ipa[name]:
|
||||
cls.ipa = True
|
||||
if cls.synth is None and is_synth[name]:
|
||||
cls.synth = True
|
||||
|
||||
|
||||
def _fill_hideable_information(classes: typing.Dict[str, schema.Class]):
|
||||
@@ -134,7 +134,7 @@ def load(m: types.ModuleType) -> schema.Schema:
|
||||
null = name
|
||||
cls.is_null_class = True
|
||||
|
||||
_fill_ipa_information(classes)
|
||||
_fill_synth_information(classes)
|
||||
_fill_hideable_information(classes)
|
||||
|
||||
return schema.Schema(includes=includes, classes=_toposort_classes_by_group(classes), null=null)
|
||||
|
||||
@@ -67,12 +67,12 @@ module Generated {
|
||||
* behavior of both the `Immediate` and non-`Immediate` versions.
|
||||
*/
|
||||
{{type}} get{{#is_unordered}}An{{/is_unordered}}Immediate{{singular}}({{#is_indexed}}int index{{/is_indexed}}) {
|
||||
{{^ipa}}
|
||||
{{^synth}}
|
||||
result = Synth::convert{{type}}FromRaw(Synth::convert{{name}}ToRaw(this){{^root}}.(Raw::{{name}}){{/root}}.{{getter}}({{#is_indexed}}index{{/is_indexed}}))
|
||||
{{/ipa}}
|
||||
{{#ipa}}
|
||||
{{/synth}}
|
||||
{{#synth}}
|
||||
none()
|
||||
{{/ipa}}
|
||||
{{/synth}}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,12 +99,12 @@ module Generated {
|
||||
{{/has_description}}
|
||||
*/
|
||||
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
|
||||
{{^ipa}}
|
||||
{{^synth}}
|
||||
{{^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}}
|
||||
{{/ipa}}
|
||||
{{#ipa}}
|
||||
{{/synth}}
|
||||
{{#synth}}
|
||||
none()
|
||||
{{/ipa}}
|
||||
{{/synth}}
|
||||
}
|
||||
|
||||
{{/type_is_hideable}}
|
||||
|
||||
@@ -15,6 +15,7 @@ module Raw {
|
||||
{{#final}}override string toString() { result = "{{name}}" }{{/final}}
|
||||
|
||||
{{#properties}}
|
||||
{{^synth}}
|
||||
/**
|
||||
* {{>ql_property_doc}} *
|
||||
{{#has_description}}
|
||||
@@ -26,6 +27,7 @@ module Raw {
|
||||
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
|
||||
{{tablename}}({{#tableparams}}{{^first}}, {{/first}}{{param}}{{/tableparams}})
|
||||
}
|
||||
{{/synth}}
|
||||
{{/properties}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// generated by {{generator}}, remove this comment if you wish to edit this file
|
||||
private import {{base_import}}
|
||||
{{#has_ipa_accessors}}
|
||||
{{#has_synth_accessors}}
|
||||
private import {{import_prefix}}.Raw
|
||||
private import {{import_prefix}}.Synth
|
||||
{{/has_ipa_accessors}}
|
||||
{{/has_synth_accessors}}
|
||||
|
||||
{{#ql_internal}}
|
||||
/**
|
||||
@@ -11,8 +11,8 @@ private import {{import_prefix}}.Synth
|
||||
*/
|
||||
{{/ql_internal}}
|
||||
class {{name}} extends Generated::{{name}} {
|
||||
{{#ipa_accessors}}
|
||||
{{#synth_accessors}}
|
||||
private
|
||||
cached {{type}} getUnderlying{{argument}}() { this = Synth::T{{name}}({{#constructorparams}}{{^first}},{{/first}}{{param}}{{/constructorparams}})}
|
||||
{{/ipa_accessors}}
|
||||
{{/synth_accessors}}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
private import {{import_prefix}}.Raw
|
||||
{{#cls}}
|
||||
{{#is_db}}
|
||||
{{#has_subtracted_ipa_types}}
|
||||
{{#has_subtracted_synth_types}}
|
||||
private import {{import_prefix}}.PureSynthConstructors
|
||||
{{/has_subtracted_ipa_types}}
|
||||
{{/has_subtracted_synth_types}}
|
||||
{{/is_db}}
|
||||
|
||||
predicate construct{{name}}({{#params}}{{^first}}, {{/first}}{{type}} {{param}}{{/params}}) {
|
||||
{{#is_db}}
|
||||
{{#subtracted_ipa_types}}{{^first}} and {{/first}}not construct{{name}}(id){{/subtracted_ipa_types}}
|
||||
{{^subtracted_ipa_types}}any(){{/subtracted_ipa_types}}
|
||||
{{#subtracted_synth_types}}{{^first}} and {{/first}}not construct{{name}}(id){{/subtracted_synth_types}}
|
||||
{{^subtracted_synth_types}}any(){{/subtracted_synth_types}}
|
||||
{{/is_db}}
|
||||
{{^is_db}}
|
||||
none()
|
||||
@@ -38,12 +38,12 @@ cached module Synth {
|
||||
* Converts a raw element to a synthesized `T{{name}}`, if possible.
|
||||
*/
|
||||
cached T{{name}} convert{{name}}FromRaw(Raw::Element e) {
|
||||
{{^is_fresh_ipa}}
|
||||
{{^is_fresh_synth}}
|
||||
result = T{{name}}(e)
|
||||
{{/is_fresh_ipa}}
|
||||
{{#is_fresh_ipa}}
|
||||
{{/is_fresh_synth}}
|
||||
{{#is_fresh_synth}}
|
||||
none()
|
||||
{{/is_fresh_ipa}}
|
||||
{{/is_fresh_synth}}
|
||||
}
|
||||
{{/final_classes}}
|
||||
|
||||
@@ -68,12 +68,12 @@ cached module Synth {
|
||||
* Converts a synthesized `T{{name}}` to a raw DB element, if possible.
|
||||
*/
|
||||
cached Raw::Element convert{{name}}ToRaw(T{{name}} e) {
|
||||
{{^is_fresh_ipa}}
|
||||
{{^is_fresh_synth}}
|
||||
e = T{{name}}(result)
|
||||
{{/is_fresh_ipa}}
|
||||
{{#is_fresh_ipa}}
|
||||
{{/is_fresh_synth}}
|
||||
{{#is_fresh_synth}}
|
||||
none()
|
||||
{{/is_fresh_ipa}}
|
||||
{{/is_fresh_synth}}
|
||||
}
|
||||
{{/final_classes}}
|
||||
|
||||
@@ -181,19 +181,19 @@ def test_cpp_skip_pragma(generate):
|
||||
]
|
||||
|
||||
|
||||
def test_ipa_classes_ignored(generate):
|
||||
def test_synth_classes_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(
|
||||
name="W",
|
||||
ipa=schema.IpaInfo(),
|
||||
synth=schema.SynthInfo(),
|
||||
),
|
||||
schema.Class(
|
||||
name="X",
|
||||
ipa=schema.IpaInfo(from_class="A"),
|
||||
synth=schema.SynthInfo(from_class="A"),
|
||||
),
|
||||
schema.Class(
|
||||
name="Y",
|
||||
ipa=schema.IpaInfo(on_arguments={"a": "A", "b": "int"}),
|
||||
synth=schema.SynthInfo(on_arguments={"a": "A", "b": "int"}),
|
||||
),
|
||||
schema.Class(
|
||||
name="Z",
|
||||
@@ -203,5 +203,27 @@ def test_ipa_classes_ignored(generate):
|
||||
]
|
||||
|
||||
|
||||
def test_synth_properties_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(
|
||||
name="X",
|
||||
properties=[
|
||||
schema.SingleProperty("x", "a"),
|
||||
schema.SingleProperty("y", "b", synth=True),
|
||||
schema.SingleProperty("z", "c"),
|
||||
schema.OptionalProperty("foo", "bar", synth=True),
|
||||
schema.RepeatedProperty("baz", "bazz", synth=True),
|
||||
schema.RepeatedOptionalProperty("bazzz", "bazzzz", synth=True),
|
||||
schema.RepeatedUnorderedProperty("bazzzzz", "bazzzzzz", synth=True),
|
||||
],
|
||||
),
|
||||
]) == [
|
||||
cpp.Class(name="X", final=True, trap_name="Xes", fields=[
|
||||
cpp.Field("x", "a"),
|
||||
cpp.Field("z", "c"),
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
|
||||
@@ -534,11 +534,11 @@ def test_null_class(generate):
|
||||
)
|
||||
|
||||
|
||||
def test_ipa_classes_ignored(generate):
|
||||
def test_synth_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"})),
|
||||
schema.Class(name="A", synth=schema.SynthInfo()),
|
||||
schema.Class(name="B", synth=schema.SynthInfo(from_class="A")),
|
||||
schema.Class(name="C", synth=schema.SynthInfo(on_arguments={"x": "A"})),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file.name,
|
||||
includes=[],
|
||||
@@ -546,10 +546,10 @@ def test_ipa_classes_ignored(generate):
|
||||
)
|
||||
|
||||
|
||||
def test_ipa_derived_classes_ignored(generate):
|
||||
def test_synth_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="B", bases=["A"], synth=schema.SynthInfo()),
|
||||
schema.Class(name="C", bases=["A"]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file.name,
|
||||
@@ -566,5 +566,32 @@ def test_ipa_derived_classes_ignored(generate):
|
||||
)
|
||||
|
||||
|
||||
def test_synth_properties_ignored(generate):
|
||||
assert generate([
|
||||
schema.Class(name="A", properties=[
|
||||
schema.SingleProperty("x", "a"),
|
||||
schema.SingleProperty("y", "b", synth=True),
|
||||
schema.SingleProperty("z", "c"),
|
||||
schema.OptionalProperty("foo", "bar", synth=True),
|
||||
schema.RepeatedProperty("baz", "bazz", synth=True),
|
||||
schema.RepeatedOptionalProperty("bazzz", "bazzzz", synth=True),
|
||||
schema.RepeatedUnorderedProperty("bazzzzz", "bazzzzzz", synth=True),
|
||||
]),
|
||||
]) == dbscheme.Scheme(
|
||||
src=schema_file.name,
|
||||
includes=[],
|
||||
declarations=[
|
||||
dbscheme.Table(
|
||||
name="as",
|
||||
columns=[
|
||||
dbscheme.Column("id", "@a", binding=True),
|
||||
dbscheme.Column("x", "a"),
|
||||
dbscheme.Column("z", "c"),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
|
||||
@@ -169,9 +169,9 @@ def test_class_without_description():
|
||||
assert prop.has_description is False
|
||||
|
||||
|
||||
def test_ipa_accessor_has_first_constructor_param_marked():
|
||||
def test_synth_accessor_has_first_constructor_param_marked():
|
||||
params = ["a", "b", "c"]
|
||||
x = ql.IpaUnderlyingAccessor("foo", "bar", params)
|
||||
x = ql.SynthUnderlyingAccessor("foo", "bar", params)
|
||||
assert x.constructorparams[0].first
|
||||
assert [p.param for p in x.constructorparams] == params
|
||||
|
||||
|
||||
@@ -848,27 +848,49 @@ def test_property_on_class_with_default_doc_name(generate_classes):
|
||||
}
|
||||
|
||||
|
||||
def test_stub_on_class_with_ipa_from_class(generate_classes):
|
||||
def test_stub_on_class_with_synth_from_class(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("MyObject", ipa=schema.IpaInfo(from_class="A")),
|
||||
schema.Class("MyObject", synth=schema.SynthInfo(from_class="A"),
|
||||
properties=[schema.SingleProperty("foo", "bar")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject", ipa_accessors=[
|
||||
ql.IpaUnderlyingAccessor(argument="Entity", type="Raw::A", constructorparams=["result"]),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject", synth_accessors=[
|
||||
ql.SynthUnderlyingAccessor(argument="Entity", type="Raw::A", constructorparams=["result"]),
|
||||
]),
|
||||
a_ql_class(name="MyObject", final=True, ipa=True)),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
}
|
||||
|
||||
|
||||
def test_stub_on_class_with_ipa_on_arguments(generate_classes):
|
||||
def test_stub_on_class_with_synth_on_arguments(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("MyObject", ipa=schema.IpaInfo(on_arguments={"base": "A", "index": "int", "label": "string"})),
|
||||
schema.Class("MyObject", synth=schema.SynthInfo(on_arguments={"base": "A", "index": "int", "label": "string"}),
|
||||
properties=[schema.SingleProperty("foo", "bar")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject", ipa_accessors=[
|
||||
ql.IpaUnderlyingAccessor(argument="Base", type="Raw::A", constructorparams=["result", "_", "_"]),
|
||||
ql.IpaUnderlyingAccessor(argument="Index", type="int", constructorparams=["_", "result", "_"]),
|
||||
ql.IpaUnderlyingAccessor(argument="Label", type="string", constructorparams=["_", "_", "result"]),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject", synth_accessors=[
|
||||
ql.SynthUnderlyingAccessor(argument="Base", type="Raw::A", constructorparams=["result", "_", "_"]),
|
||||
ql.SynthUnderlyingAccessor(argument="Index", type="int", constructorparams=["_", "result", "_"]),
|
||||
ql.SynthUnderlyingAccessor(argument="Label", type="string", constructorparams=["_", "_", "result"]),
|
||||
]),
|
||||
a_ql_class(name="MyObject", final=True, ipa=True)),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
}
|
||||
|
||||
|
||||
def test_synth_property(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "bar", synth=True)]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ def test_class_with_pragmas():
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_from_class():
|
||||
def test_synth_from_class():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
@@ -348,12 +348,12 @@ def test_ipa_from_class():
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=True),
|
||||
'B': schema.Class('B', bases=['A'], ipa=schema.IpaInfo(from_class="A")),
|
||||
'A': schema.Class('A', derived={'B'}, synth=True),
|
||||
'B': schema.Class('B', bases=['A'], synth=schema.SynthInfo(from_class="A")),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_from_class_ref():
|
||||
def test_synth_from_class_ref():
|
||||
@load
|
||||
class data:
|
||||
@defs.synth.from_class("B")
|
||||
@@ -364,12 +364,12 @@ def test_ipa_from_class_ref():
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=schema.IpaInfo(from_class="B")),
|
||||
'A': schema.Class('A', derived={'B'}, synth=schema.SynthInfo(from_class="B")),
|
||||
'B': schema.Class('B', bases=['A']),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_from_class_dangling():
|
||||
def test_synth_from_class_dangling():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
@@ -378,7 +378,7 @@ def test_ipa_from_class_dangling():
|
||||
pass
|
||||
|
||||
|
||||
def test_ipa_class_on():
|
||||
def test_synth_class_on():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
@@ -389,12 +389,12 @@ def test_ipa_class_on():
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=True),
|
||||
'B': schema.Class('B', bases=['A'], ipa=schema.IpaInfo(on_arguments={'a': 'A', 'i': 'int'})),
|
||||
'A': schema.Class('A', derived={'B'}, synth=True),
|
||||
'B': schema.Class('B', bases=['A'], synth=schema.SynthInfo(on_arguments={'a': 'A', 'i': 'int'})),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_class_on_ref():
|
||||
def test_synth_class_on_ref():
|
||||
class A:
|
||||
pass
|
||||
|
||||
@@ -408,12 +408,12 @@ def test_ipa_class_on_ref():
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=schema.IpaInfo(on_arguments={'b': 'B', 'i': 'int'})),
|
||||
'A': schema.Class('A', derived={'B'}, synth=schema.SynthInfo(on_arguments={'b': 'B', 'i': 'int'})),
|
||||
'B': schema.Class('B', bases=['A']),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_class_on_dangling():
|
||||
def test_synth_class_on_dangling():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
@@ -422,7 +422,7 @@ def test_ipa_class_on_dangling():
|
||||
pass
|
||||
|
||||
|
||||
def test_ipa_class_hierarchy():
|
||||
def test_synth_class_hierarchy():
|
||||
@load
|
||||
class data:
|
||||
class Root:
|
||||
@@ -447,14 +447,25 @@ def test_ipa_class_hierarchy():
|
||||
|
||||
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')),
|
||||
'Base': schema.Class('Base', bases=['Root'], derived={'Intermediate', 'B'}, synth=True),
|
||||
'Intermediate': schema.Class('Intermediate', bases=['Base'], derived={'A'}, synth=True),
|
||||
'A': schema.Class('A', bases=['Intermediate'], synth=schema.SynthInfo(on_arguments={'a': 'Base', 'i': 'int'})),
|
||||
'B': schema.Class('B', bases=['Base'], synth=schema.SynthInfo(from_class='Base')),
|
||||
'C': schema.Class('C', bases=['Root']),
|
||||
}
|
||||
|
||||
|
||||
def test_synthesized_property():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: defs.int | defs.synth
|
||||
|
||||
assert data.classes["A"].properties == [
|
||||
schema.SingleProperty("x", "int", synth=True)
|
||||
]
|
||||
|
||||
|
||||
def test_class_docstring():
|
||||
@load
|
||||
class data:
|
||||
|
||||
Reference in New Issue
Block a user