mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Swift: split generated C++ code into .h and .cpp
This commit is contained in:
@@ -78,4 +78,4 @@ def generate(opts, renderer):
|
||||
assert opts.cpp_output
|
||||
processor = Processor({cls.name: cls for cls in schema.load(opts.schema).classes})
|
||||
out = opts.cpp_output
|
||||
renderer.render(cpp.ClassList(processor.get_classes(), opts.schema), out / f"TrapClasses.h")
|
||||
renderer.render(cpp.ClassList(processor.get_classes(), opts.schema), out / "TrapClasses")
|
||||
|
||||
@@ -80,8 +80,7 @@ def generate(opts, renderer):
|
||||
for d in e.rhs:
|
||||
tag_graph.setdefault(d.type, set()).add(e.lhs)
|
||||
|
||||
renderer.render(cpp.TrapList(traps, opts.dbscheme),
|
||||
out / f"TrapEntries.h")
|
||||
renderer.render(cpp.TrapList(traps, opts.dbscheme), out / "TrapEntries")
|
||||
|
||||
tags = []
|
||||
for index, tag in enumerate(toposort_flatten(tag_graph)):
|
||||
@@ -91,4 +90,4 @@ def generate(opts, renderer):
|
||||
index=index,
|
||||
id=tag,
|
||||
))
|
||||
renderer.render(cpp.TagList(tags, opts.dbscheme), out / f"TrapTags.h")
|
||||
renderer.render(cpp.TagList(tags, opts.dbscheme), out / "TrapTags")
|
||||
|
||||
@@ -99,7 +99,7 @@ class Tag:
|
||||
@dataclass
|
||||
class TrapList:
|
||||
template: ClassVar = 'trap_traps'
|
||||
|
||||
extensions = ["h", "cpp"]
|
||||
traps: List[Trap]
|
||||
source: str
|
||||
|
||||
@@ -107,6 +107,7 @@ class TrapList:
|
||||
@dataclass
|
||||
class TagList:
|
||||
template: ClassVar = 'trap_tags'
|
||||
extensions = ["h"]
|
||||
|
||||
tags: List[Tag]
|
||||
source: str
|
||||
@@ -143,6 +144,7 @@ class Class:
|
||||
@dataclass
|
||||
class ClassList:
|
||||
template: ClassVar = "cpp_classes"
|
||||
extensions: ClassVar = ["h", "cpp"]
|
||||
|
||||
classes: List[Class]
|
||||
source: str
|
||||
|
||||
@@ -28,18 +28,23 @@ class Renderer:
|
||||
|
||||
`data` must have a `template` attribute denoting which template to use from the template directory.
|
||||
|
||||
If the file is unchanged, then no write is performed (and `done_something` remains unchanged)
|
||||
|
||||
If `guard_base` is provided, it must be a path at the root of `output` and a header guard will be injected in
|
||||
the template based off of the relative path of `output` in `guard_base`
|
||||
Optionally, `data` can also have an `extensions` attribute denoting list of file extensions: they will all be
|
||||
appended to the template name with an underscore and be generated in turn.
|
||||
"""
|
||||
mnemonic = type(data).__name__
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
data = self._r.render_name(data.template, data, generator=self._generator)
|
||||
with open(output, "w") as out:
|
||||
out.write(data)
|
||||
log.debug(f"generated {mnemonic} {output.name}")
|
||||
self.written.add(output)
|
||||
extensions = getattr(data, "extensions", [None])
|
||||
for ext in extensions:
|
||||
output_filename = output
|
||||
template = data.template
|
||||
if ext:
|
||||
output_filename = output_filename.with_suffix(f".{ext}")
|
||||
template += f"_{ext}"
|
||||
contents = self._r.render_name(template, data, generator=self._generator)
|
||||
with open(output_filename, "w") as out:
|
||||
out.write(contents)
|
||||
log.debug(f"{mnemonic}: generated {output.name}")
|
||||
self.written.add(output_filename)
|
||||
|
||||
def cleanup(self, existing):
|
||||
""" Remove files in `existing` for which no `render` has been called """
|
||||
|
||||
37
swift/codegen/templates/cpp_classes_cpp.mustache
Normal file
37
swift/codegen/templates/cpp_classes_cpp.mustache
Normal file
@@ -0,0 +1,37 @@
|
||||
// generated by {{generator}} from {{source}}
|
||||
// clang-format off
|
||||
#include "./TrapClasses.h"
|
||||
|
||||
namespace codeql {
|
||||
{{#classes}}
|
||||
|
||||
void {{name}}::emit({{^final}}TrapLabel<{{name}}Tag> id, {{/final}}std::ostream& out) const {
|
||||
{{#trap_name}}
|
||||
out << {{.}}Trap{id{{#single_fields}}, {{field_name}}{{/single_fields}}} << '\n';
|
||||
{{/trap_name}}
|
||||
{{#bases}}
|
||||
{{ref.name}}::emit(id, out);
|
||||
{{/bases}}
|
||||
{{#fields}}
|
||||
{{#is_predicate}}
|
||||
if ({{field_name}}) out << {{trap_name}}Trap{id} << '\n';
|
||||
{{/is_predicate}}
|
||||
{{#is_optional}}
|
||||
{{^is_repeated}}
|
||||
if ({{field_name}}) out << {{trap_name}}Trap{id, *{{field_name}}} << '\n';
|
||||
{{/is_repeated}}
|
||||
{{/is_optional}}
|
||||
{{#is_repeated}}
|
||||
for (auto i = 0u; i < {{field_name}}.size(); ++i) {
|
||||
{{^is_optional}}
|
||||
out << {{trap_name}}Trap{id, i, {{field_name}}[i]};
|
||||
{{/is_optional}}
|
||||
{{#is_optional}}
|
||||
if ({{field_name}}[i]) out << {{trap_name}}Trap{id, i, *{{field_name}}[i]};
|
||||
{{/is_optional}}
|
||||
}
|
||||
{{/is_repeated}}
|
||||
{{/fields}}
|
||||
}
|
||||
{{/classes}}
|
||||
}
|
||||
@@ -31,34 +31,7 @@ struct {{name}}{{#has_bases}} : {{#bases}}{{^first}}, {{/first}}{{ref.name}}{{/b
|
||||
{{/final}}
|
||||
|
||||
protected:
|
||||
void emit({{^final}}TrapLabel<{{name}}Tag> id, {{/final}}std::ostream& out) const {
|
||||
{{#trap_name}}
|
||||
out << {{.}}Trap{id{{#single_fields}}, {{field_name}}{{/single_fields}}} << '\n';
|
||||
{{/trap_name}}
|
||||
{{#bases}}
|
||||
{{ref.name}}::emit(id, out);
|
||||
{{/bases}}
|
||||
{{#fields}}
|
||||
{{#is_predicate}}
|
||||
if ({{field_name}}) out << {{trap_name}}Trap{id} << '\n';
|
||||
{{/is_predicate}}
|
||||
{{#is_optional}}
|
||||
{{^is_repeated}}
|
||||
if ({{field_name}}) out << {{trap_name}}Trap{id, *{{field_name}}} << '\n';
|
||||
{{/is_repeated}}
|
||||
{{/is_optional}}
|
||||
{{#is_repeated}}
|
||||
for (auto i = 0u; i < {{field_name}}.size(); ++i) {
|
||||
{{^is_optional}}
|
||||
out << {{trap_name}}Trap{id, i, {{field_name}}[i]};
|
||||
{{/is_optional}}
|
||||
{{#is_optional}}
|
||||
if ({{field_name}}[i]) out << {{trap_name}}Trap{id, i, *{{field_name}}[i]};
|
||||
{{/is_optional}}
|
||||
}
|
||||
{{/is_repeated}}
|
||||
{{/fields}}
|
||||
}
|
||||
void emit({{^final}}TrapLabel<{{name}}Tag> id, {{/final}}std::ostream& out) const;
|
||||
};
|
||||
|
||||
template <>
|
||||
15
swift/codegen/templates/trap_traps_cpp.mustache
Normal file
15
swift/codegen/templates/trap_traps_cpp.mustache
Normal file
@@ -0,0 +1,15 @@
|
||||
// generated by {{generator}} from {{source}}
|
||||
// clang-format off
|
||||
#include "./TrapEntries.h"
|
||||
|
||||
namespace codeql {
|
||||
{{#traps}}
|
||||
|
||||
// {{table_name}}
|
||||
std::ostream &operator<<(std::ostream &out, const {{name}}Trap &e) {
|
||||
out << "{{table_name}}("{{#fields}}{{^first}} << ", "{{/first}}
|
||||
<< {{#get_streamer}}e.{{field_name}}{{/get_streamer}}{{/fields}} << ")";
|
||||
return out;
|
||||
}
|
||||
{{/traps}}
|
||||
}
|
||||
@@ -14,21 +14,12 @@ namespace codeql {
|
||||
|
||||
// {{table_name}}
|
||||
struct {{name}}Trap {
|
||||
static constexpr bool is_binding = {{#id}}true{{/id}}{{^id}}false{{/id}};
|
||||
{{#id}}
|
||||
{{type}} getBoundLabel() const { return {{field_name}}; }
|
||||
{{/id}}
|
||||
|
||||
{{#fields}}
|
||||
{{type}} {{field_name}}{};
|
||||
{{/fields}}
|
||||
};
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &out, const {{name}}Trap &e) {
|
||||
out << "{{table_name}}("{{#fields}}{{^first}} << ", "{{/first}}
|
||||
<< {{#get_streamer}}e.{{field_name}}{{/get_streamer}}{{/fields}} << ")";
|
||||
return out;
|
||||
}
|
||||
std::ostream &operator<<(std::ostream &out, const {{name}}Trap &e);
|
||||
{{#id}}
|
||||
|
||||
namespace detail {
|
||||
@@ -14,8 +14,8 @@ def generate(opts, renderer, input):
|
||||
def ret(classes):
|
||||
input.classes = classes
|
||||
generated = run_generation(cppgen.generate, opts, renderer)
|
||||
assert set(generated) == {output_dir / "TrapClasses.h"}
|
||||
generated = generated[output_dir / "TrapClasses.h"]
|
||||
assert set(generated) == {output_dir / "TrapClasses"}
|
||||
generated = generated[output_dir / "TrapClasses"]
|
||||
assert isinstance(generated, cpp.ClassList)
|
||||
return generated.classes
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
from unittest import mock
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -38,7 +39,7 @@ def test_constructor(pystache_renderer_cls, sut):
|
||||
|
||||
|
||||
def test_render(pystache_renderer, sut):
|
||||
data = mock.Mock()
|
||||
data = mock.Mock(spec=("template",))
|
||||
output = mock.Mock()
|
||||
with mock.patch("builtins.open", mock.mock_open()) as output_stream:
|
||||
sut.render(data, output)
|
||||
@@ -54,8 +55,33 @@ def test_render(pystache_renderer, sut):
|
||||
assert sut.written == {output}
|
||||
|
||||
|
||||
def test_render_with_extensions(pystache_renderer, sut):
|
||||
data = mock.Mock(spec=("template", "extensions"))
|
||||
data.template = "test_template"
|
||||
data.extensions = ["foo", "bar", "baz"]
|
||||
output = pathlib.Path("my", "test", "file")
|
||||
expected_outputs = [pathlib.Path("my", "test", p) for p in ("file.foo", "file.bar", "file.baz")]
|
||||
rendered = [object() for _ in expected_outputs]
|
||||
pystache_renderer.render_name.side_effect = rendered
|
||||
with mock.patch("builtins.open", mock.mock_open()) as output_stream:
|
||||
sut.render(data, output)
|
||||
expected_templates = ["test_template_foo", "test_template_bar", "test_template_baz"]
|
||||
assert pystache_renderer.mock_calls == [
|
||||
mock.call.render_name(t, data, generator=generator) for t in expected_templates
|
||||
]
|
||||
expected_calls = []
|
||||
for contents, out in zip(rendered, expected_outputs):
|
||||
expected_calls.extend((
|
||||
mock.call(out, 'w'),
|
||||
mock.call().__enter__(),
|
||||
mock.call().write(contents),
|
||||
mock.call().__exit__(None, None, None),
|
||||
))
|
||||
assert sut.written == set(expected_outputs)
|
||||
|
||||
|
||||
def test_written(sut):
|
||||
data = [mock.Mock() for _ in range(4)]
|
||||
data = [mock.Mock(spec=("template",)) for _ in range(4)]
|
||||
output = [mock.Mock() for _ in data]
|
||||
with mock.patch("builtins.open", mock.mock_open()) as output_stream:
|
||||
for d, o in zip(data, output):
|
||||
@@ -64,7 +90,7 @@ def test_written(sut):
|
||||
|
||||
|
||||
def test_cleanup(sut):
|
||||
data = [mock.Mock() for _ in range(4)]
|
||||
data = [mock.Mock(spec=("template",)) for _ in range(4)]
|
||||
output = [mock.Mock() for _ in data]
|
||||
with mock.patch("builtins.open", mock.mock_open()) as output_stream:
|
||||
for d, o in zip(data, output):
|
||||
|
||||
@@ -15,8 +15,8 @@ def generate(opts, renderer, dbscheme_input):
|
||||
dbscheme_input.entities = entities
|
||||
generated = run_generation(trapgen.generate, opts, renderer)
|
||||
assert set(generated) == {output_dir /
|
||||
"TrapEntries.h", output_dir / "TrapTags.h"}
|
||||
return generated[output_dir / "TrapEntries.h"], generated[output_dir / "TrapTags.h"]
|
||||
"TrapEntries", output_dir / "TrapTags"}
|
||||
return generated[output_dir / "TrapEntries"], generated[output_dir / "TrapTags"]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
Reference in New Issue
Block a user