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:
|
||||
cls = self._classmap[name]
|
||||
return rust.Class(
|
||||
name=name,
|
||||
fields=[
|
||||
_get_field(c, p)
|
||||
properties = [
|
||||
(c, p)
|
||||
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 [],
|
||||
]
|
||||
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(
|
||||
name=name,
|
||||
fields=fields,
|
||||
detached_fields=detached_fields,
|
||||
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,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import dataclasses
|
||||
import re
|
||||
import typing
|
||||
import inflection
|
||||
|
||||
# taken from https://doc.rust-lang.org/reference/keywords.html
|
||||
keywords = {
|
||||
@@ -106,12 +107,17 @@ class Field:
|
||||
def is_label(self):
|
||||
return self.base_type == "trap::Label"
|
||||
|
||||
@property
|
||||
def singular_field_name(self) -> str:
|
||||
return inflection.singularize(self.field_name.rstrip("_"))
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Class:
|
||||
name: str
|
||||
entry_table: str | None = None
|
||||
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)
|
||||
|
||||
@property
|
||||
@@ -128,6 +134,10 @@ class Class:
|
||||
ret.setdefault(f.table_name, []).append(f)
|
||||
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
|
||||
class ClassList:
|
||||
|
||||
@@ -239,6 +239,7 @@ ql.add(_Pragma("internal"))
|
||||
|
||||
cpp.add(_Pragma("skip"))
|
||||
|
||||
rust.add(_Pragma("detach"))
|
||||
rust.add(_Pragma("skip_doc_test"))
|
||||
|
||||
rust.add(_ParametrizedClassPragma("doc_test_signature", lambda signature: signature))
|
||||
|
||||
@@ -59,6 +59,16 @@ pub struct {{name}} {
|
||||
_unused: ()
|
||||
}
|
||||
{{/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}} {
|
||||
fn class_name() -> &'static str { "{{name}}" }
|
||||
|
||||
Reference in New Issue
Block a user