mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Rust/Codegen: allow to "detach" property emission
By using the `rust.detach` pragma on a property, we make that property not appear in the generated struct as a field, and provide instead a `generated::Class::emit_property` function that can be used to emit the corresponding TRAP entry independently.
This commit is contained in:
@@ -72,13 +72,28 @@ class Processor:
|
|||||||
|
|
||||||
def _get_class(self, name: str) -> rust.Class:
|
def _get_class(self, name: str) -> rust.Class:
|
||||||
cls = self._classmap[name]
|
cls = self._classmap[name]
|
||||||
|
properties = [
|
||||||
|
(c, p)
|
||||||
|
for c, p in _get_properties(cls, self._classmap)
|
||||||
|
if "rust_skip" not in p.pragmas and not p.synth
|
||||||
|
]
|
||||||
|
fields = []
|
||||||
|
detached_fields = []
|
||||||
|
for c, p in properties:
|
||||||
|
if "rust_detach" in p.pragmas:
|
||||||
|
# only generate detached fields in the actual class defining them, not the derived ones
|
||||||
|
if c is cls:
|
||||||
|
# TODO lift this restriction if required (requires change in dbschemegen as well)
|
||||||
|
assert c.derived or not p.is_single, \
|
||||||
|
f"property {p.name} in concrete class marked as detached but not optional"
|
||||||
|
detached_fields.append(_get_field(c, p))
|
||||||
|
elif not cls.derived:
|
||||||
|
# for non-detached ones, only generate fields in the concrete classes
|
||||||
|
fields.append(_get_field(c, p))
|
||||||
return rust.Class(
|
return rust.Class(
|
||||||
name=name,
|
name=name,
|
||||||
fields=[
|
fields=fields,
|
||||||
_get_field(c, p)
|
detached_fields=detached_fields,
|
||||||
for c, p in _get_properties(cls, self._classmap)
|
|
||||||
if "rust_skip" not in p.pragmas and not p.synth
|
|
||||||
] if not cls.derived else [],
|
|
||||||
ancestors=sorted(set(a.name for a in _get_ancestors(cls, self._classmap))),
|
ancestors=sorted(set(a.name for a in _get_ancestors(cls, self._classmap))),
|
||||||
entry_table=inflection.tableize(cls.name) if not cls.derived else None,
|
entry_table=inflection.tableize(cls.name) if not cls.derived else None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import re
|
import re
|
||||||
import typing
|
import typing
|
||||||
|
import inflection
|
||||||
|
|
||||||
# taken from https://doc.rust-lang.org/reference/keywords.html
|
# taken from https://doc.rust-lang.org/reference/keywords.html
|
||||||
keywords = {
|
keywords = {
|
||||||
@@ -106,12 +107,17 @@ class Field:
|
|||||||
def is_label(self):
|
def is_label(self):
|
||||||
return self.base_type == "trap::Label"
|
return self.base_type == "trap::Label"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def singular_field_name(self) -> str:
|
||||||
|
return inflection.singularize(self.field_name.rstrip("_"))
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Class:
|
class Class:
|
||||||
name: str
|
name: str
|
||||||
entry_table: str | None = None
|
entry_table: str | None = None
|
||||||
fields: list[Field] = dataclasses.field(default_factory=list)
|
fields: list[Field] = dataclasses.field(default_factory=list)
|
||||||
|
detached_fields: list[Field] = dataclasses.field(default_factory=list)
|
||||||
ancestors: list[str] = dataclasses.field(default_factory=list)
|
ancestors: list[str] = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -128,6 +134,10 @@ class Class:
|
|||||||
ret.setdefault(f.table_name, []).append(f)
|
ret.setdefault(f.table_name, []).append(f)
|
||||||
return [{"table_name": k, "fields": v} for k, v in ret.items()]
|
return [{"table_name": k, "fields": v} for k, v in ret.items()]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_detached_fields(self) -> bool:
|
||||||
|
return bool(self.detached_fields)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class ClassList:
|
class ClassList:
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ ql.add(_Pragma("internal"))
|
|||||||
|
|
||||||
cpp.add(_Pragma("skip"))
|
cpp.add(_Pragma("skip"))
|
||||||
|
|
||||||
|
rust.add(_Pragma("detach"))
|
||||||
rust.add(_Pragma("skip_doc_test"))
|
rust.add(_Pragma("skip_doc_test"))
|
||||||
|
|
||||||
rust.add(_ParametrizedClassPragma("doc_test_signature", lambda signature: signature))
|
rust.add(_ParametrizedClassPragma("doc_test_signature", lambda signature: signature))
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ pub struct {{name}} {
|
|||||||
_unused: ()
|
_unused: ()
|
||||||
}
|
}
|
||||||
{{/is_entry}}
|
{{/is_entry}}
|
||||||
|
{{#has_detached_fields}}
|
||||||
|
|
||||||
|
impl {{name}} {
|
||||||
|
{{#detached_fields}}
|
||||||
|
pub fn emit_{{singular_field_name}}(id: trap::Label<Self>, {{#is_repeated}}{{^is_unordered}}i: usize, {{/is_unordered}}{{/is_repeated}}value: {{base_type}}, out: &mut trap::Writer) {
|
||||||
|
out.add_tuple("{{table_name}}", vec![id.into(), {{#is_repeated}}{{^is_unordered}}i.into(), {{/is_unordered}}{{/is_repeated}}value.into()]);
|
||||||
|
}
|
||||||
|
{{/detached_fields}}
|
||||||
|
}
|
||||||
|
{{/has_detached_fields}}
|
||||||
|
|
||||||
impl trap::TrapClass for {{name}} {
|
impl trap::TrapClass for {{name}} {
|
||||||
fn class_name() -> &'static str { "{{name}}" }
|
fn class_name() -> &'static str { "{{name}}" }
|
||||||
|
|||||||
Reference in New Issue
Block a user