Skip to content

Codegen: allow marking properties as internal #14902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions misc/codegen/generators/qlgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
description=prop.description,
synth=bool(cls.synth) or prop.synth,
type_is_hideable=lookup[prop.type].hideable if prop.type in lookup else False,
internal="ql_internal" in prop.pragmas,
)
if prop.is_single:
args.update(
Expand Down Expand Up @@ -151,7 +152,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic


def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> ql.Class:
pragmas = {k: True for k in cls.pragmas if k.startswith("ql")}
pragmas = {k: True for k in cls.pragmas if k.startswith("qltest")}
prev_child = ""
properties = []
for p in cls.properties:
Expand All @@ -167,6 +168,7 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
dir=pathlib.Path(cls.group or ""),
doc=cls.doc,
hideable=cls.hideable,
internal="ql_internal" in cls.pragmas,
**pragmas,
)

Expand Down Expand Up @@ -313,7 +315,7 @@ def _get_stub(cls: schema.Class, base_import: str, generated_import_prefix: str)
accessors = []
return ql.Stub(name=cls.name, base_import=base_import, import_prefix=generated_import_prefix,
doc=cls.doc, synth_accessors=accessors,
ql_internal="ql_internal" in cls.pragmas)
internal="ql_internal" in cls.pragmas)


_stub_qldoc_header = "// the following QLdoc is generated: if you need to edit it, do it in the schema file\n"
Expand Down Expand Up @@ -397,15 +399,15 @@ def generate(opts, renderer):
_patch_class_qldoc(c.name, qldoc, stub_file)

# for example path/to/elements -> path/to/elements.qll
renderer.render(ql.ImportList([i for name, i in imports.items() if not classes[name].ql_internal]),
renderer.render(ql.ImportList([i for name, i in imports.items() if not classes[name].internal]),
include_file)

elements_module = get_import(include_file, opts.root_dir)

renderer.render(
ql.GetParentImplementation(
classes=list(classes.values()),
imports=[elements_module] + [i for name, i in imports.items() if classes[name].ql_internal],
imports=[elements_module] + [i for name, i in imports.items() if classes[name].internal],
),
out / 'ParentChild.qll')

Expand Down
11 changes: 4 additions & 7 deletions misc/codegen/lib/ql.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Property:
doc_plural: Optional[str] = None
synth: bool = False
type_is_hideable: bool = False
internal: bool = False

def __post_init__(self):
if self.tableparams:
Expand Down Expand Up @@ -80,10 +81,6 @@ def is_single(self):
def is_child(self):
return self.prev_child is not None

@property
def has_description(self) -> bool:
return bool(self.description)

@property
def is_indexed(self) -> bool:
return self.is_repeated and not self.is_unordered
Expand Down Expand Up @@ -112,7 +109,7 @@ class Class:
qltest_skip: bool = False
qltest_collapse_hierarchy: bool = False
qltest_uncollapse_hierarchy: bool = False
ql_internal: bool = False
internal: bool = False
doc: List[str] = field(default_factory=list)
hideable: bool = False

Expand Down Expand Up @@ -162,7 +159,7 @@ class Stub:
base_import: str
import_prefix: str
synth_accessors: List[SynthUnderlyingAccessor] = field(default_factory=list)
ql_internal: bool = False
internal: bool = False
doc: List[str] = field(default_factory=list)

@property
Expand All @@ -171,7 +168,7 @@ def has_synth_accessors(self) -> bool:

@property
def has_qldoc(self) -> bool:
return bool(self.doc) or self.ql_internal
return bool(self.doc) or self.internal


@dataclass
Expand Down
25 changes: 21 additions & 4 deletions misc/codegen/templates/ql_class.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ module Generated {
* {{>ql_property_doc}} *
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
* behavior of both the `Immediate` and non-`Immediate` versions.
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
{{type}} get{{#is_unordered}}An{{/is_unordered}}Immediate{{singular}}({{#is_indexed}}int index{{/is_indexed}}) {
{{^synth}}
Expand All @@ -83,11 +86,12 @@ module Generated {

/**
* {{>ql_property_doc}} *
{{#has_description}}
{{#description}}
* {{.}}
{{/description}}
{{/has_description}}
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
final {{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
exists({{type}} immediate | immediate = this.get{{#is_unordered}}An{{/is_unordered}}Immediate{{singular}}({{#is_indexed}}index{{/is_indexed}}) and
Expand All @@ -98,11 +102,12 @@ module Generated {
{{^type_is_hideable}}
/**
* {{>ql_property_doc}} *
{{#has_description}}
{{#description}}
* {{.}}
{{/description}}
{{/has_description}}
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
{{^synth}}
Expand All @@ -117,6 +122,9 @@ module Generated {
{{#is_optional}}
/**
* Holds if `{{getter}}({{#is_repeated}}index{{/is_repeated}})` exists.
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
final predicate has{{singular}}({{#is_repeated}}int index{{/is_repeated}}) {
exists(this.{{getter}}({{#is_repeated}}index{{/is_repeated}}))
Expand All @@ -126,6 +134,9 @@ module Generated {

/**
* Gets any of the {{doc_plural}}.
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
final {{type}} {{indefinite_getter}}() {
result = this.{{getter}}(_)
Expand All @@ -134,6 +145,9 @@ module Generated {

/**
* Gets the number of {{doc_plural}}.
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
final int getNumberOf{{plural}}() {
result = count(int i | exists(this.{{getter}}(i)))
Expand All @@ -143,6 +157,9 @@ module Generated {
{{#is_unordered}}
/**
* Gets the number of {{doc_plural}}.
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
final int getNumberOf{{plural}}() {
result = count(this.{{getter}}())
Expand Down
2 changes: 0 additions & 2 deletions misc/codegen/templates/ql_db.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ module Raw {
{{^synth}}
/**
* {{>ql_property_doc}} *
{{#has_description}}
{{#description}}
* {{.}}
{{/description}}
{{/has_description}}
*/
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
{{tablename}}({{#tableparams}}{{^first}}, {{/first}}{{param}}{{/tableparams}})
Expand Down
4 changes: 2 additions & 2 deletions misc/codegen/templates/ql_stub_class_qldoc.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
{{#doc}}
* {{.}}
{{/doc}}
{{#ql_internal}}
{{#internal}}
* INTERNAL: Do not use.
{{/ql_internal}}
{{/internal}}
*/
{{/has_qldoc}}
4 changes: 2 additions & 2 deletions misc/codegen/templates/ql_stub_module_qldoc.mustache
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* This module provides a hand-modifiable wrapper around the generated class `{{name}}`.
{{#ql_internal}}
{{#internal}}
* INTERNAL: Do not use.
{{/ql_internal}}
{{/internal}}
*/
16 changes: 3 additions & 13 deletions misc/codegen/test/test_ql.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,28 +147,18 @@ def test_class_with_children():
assert cls.has_children is True


@pytest.mark.parametrize("doc,ql_internal,expected",
@pytest.mark.parametrize("doc,internal,expected",
[
(["foo", "bar"], False, True),
(["foo", "bar"], True, True),
([], False, False),
([], True, True),
])
def test_has_doc(doc, ql_internal, expected):
stub = ql.Stub("Class", base_import="foo", import_prefix="bar", doc=doc, ql_internal=ql_internal)
def test_has_doc(doc, internal, expected):
stub = ql.Stub("Class", base_import="foo", import_prefix="bar", doc=doc, internal=internal)
assert stub.has_qldoc is expected


def test_property_with_description():
prop = ql.Property("X", "int", description=["foo", "bar"])
assert prop.has_description is True


def test_class_without_description():
prop = ql.Property("X", "int")
assert prop.has_description is False


def test_synth_accessor_has_first_constructor_param_marked():
params = ["a", "b", "c"]
x = ql.SynthUnderlyingAccessor("foo", "bar", params)
Expand Down
48 changes: 33 additions & 15 deletions misc/codegen/test/test_qlgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ def test_one_empty_internal_class(generate_classes):
assert generate_classes([
schema.Class("A", pragmas=["ql_internal"])
]) == {
"A.qll": (a_ql_stub(name="A", ql_internal=True),
a_ql_class(name="A", final=True, ql_internal=True)),
"A.qll": (a_ql_stub(name="A", internal=True),
a_ql_class(name="A", final=True, internal=True)),
}


Expand Down Expand Up @@ -202,11 +202,11 @@ def test_hierarchy_children(generate_children_implementations):
schema.Class("C", bases=["A"], derived={"D"}, pragmas=["ql_internal"]),
schema.Class("D", bases=["B", "C"]),
]) == ql.GetParentImplementation(
classes=[a_ql_class(name="A", ql_internal=True),
classes=[a_ql_class(name="A", internal=True),
a_ql_class(name="B", bases=["A"], imports=[
stub_import_prefix + "A"]),
a_ql_class(name="C", bases=["A"], imports=[
stub_import_prefix + "A"], ql_internal=True),
stub_import_prefix + "A"], internal=True),
a_ql_class(name="D", final=True, bases=["B", "C"],
imports=[stub_import_prefix + cls for cls in "BC"]),
],
Expand All @@ -228,6 +228,21 @@ def test_single_property(generate_classes):
}


def test_internal_property(generate_classes):
assert generate_classes([
schema.Class("MyObject", properties=[
schema.SingleProperty("foo", "bar", pragmas=["ql_internal"])]),
]) == {
"MyObject.qll": (a_ql_stub(name="MyObject"),
a_ql_class(name="MyObject", final=True,
properties=[
ql.Property(singular="Foo", type="bar", tablename="my_objects",
tableparams=["this", "result"], doc="foo of this my object",
internal=True),
])),
}


def test_children(generate_classes):
assert generate_classes([
schema.Class("FakeRoot"),
Expand Down Expand Up @@ -424,7 +439,8 @@ def test_class_dir(generate_classes):
schema.Class("A", derived={"B"}, group=dir),
schema.Class("B", bases=["A"]),
]) == {
f"{dir}/A.qll": (a_ql_stub(name="A", import_prefix="another.rel.path."), a_ql_class(name="A", dir=pathlib.Path(dir))),
f"{dir}/A.qll": (
a_ql_stub(name="A", import_prefix="another.rel.path."), a_ql_class(name="A", dir=pathlib.Path(dir))),
"B.qll": (a_ql_stub(name="B"),
a_ql_class(name="B", final=True, bases=["A"],
imports=[stub_import_prefix + "another.rel.path.A"])),
Expand Down Expand Up @@ -878,9 +894,9 @@ def test_stub_on_class_with_synth_from_class(generate_classes):
ql.SynthUnderlyingAccessor(argument="Entity", type="Raw::A", constructorparams=["result"]),
]),
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"),
])),
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
tableparams=["this", "result"], doc="foo of this my object"),
])),
}


Expand All @@ -895,9 +911,9 @@ def test_stub_on_class_with_synth_on_arguments(generate_classes):
ql.SynthUnderlyingAccessor(argument="Label", type="string", constructorparams=["_", "_", "result"]),
]),
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"),
])),
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
tableparams=["this", "result"], doc="foo of this my object"),
])),
}


Expand All @@ -909,7 +925,8 @@ def test_synth_property(generate_classes):
"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,
ql.Property(singular="Foo", type="bar", tablename="my_objects",
synth=True,
tableparams=["this", "result"], doc="foo of this my object"),
])),
}
Expand All @@ -934,9 +951,10 @@ def test_hideable_property(generate_classes):
"Other.qll": (a_ql_stub(name="Other"),
a_ql_class(name="Other", imports=[stub_import_prefix + "MyObject"],
final=True, properties=[
ql.Property(singular="X", type="MyObject", tablename="others", type_is_hideable=True,
tableparams=["this", "result"], doc="x of this other"),
])),
ql.Property(singular="X", type="MyObject", tablename="others",
type_is_hideable=True,
tableparams=["this", "result"], doc="x of this other"),
])),
}


Expand Down
4 changes: 2 additions & 2 deletions swift/ql/.generated.list

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions swift/ql/lib/codeql/swift/generated/MacroRole.qll

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading