diff options
Diffstat (limited to 'source/20-codegen-compiler')
-rw-r--r-- | source/20-codegen-compiler/CodegenModel.cpp | 295 | ||||
-rw-r--r-- | source/20-codegen-compiler/SQLiteHelper.hpp | 204 | ||||
-rw-r--r-- | source/20-codegen-compiler/main.cpp | 19 |
3 files changed, 297 insertions, 221 deletions
diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp index da7a5cc..303ad4e 100644 --- a/source/20-codegen-compiler/CodegenModel.cpp +++ b/source/20-codegen-compiler/CodegenModel.cpp @@ -1,6 +1,7 @@ #include "CodegenModel.hpp" #include "CodegenUtils.hpp" +#include "SQLiteHelper.hpp" #include <Macros.hpp> #include <ScopeGuard.hpp> @@ -34,55 +35,6 @@ public: #define CURRENT_DATABASE_VERSION 1 constexpr int64_t kGlobalNamespaceId = 1; -struct SQLiteDatabase { - sqlite3* database = nullptr; - - ~SQLiteDatabase() { - // NOTE: calling with NULL is a harmless no-op - int result = sqlite3_close(database); - assert(result == SQLITE_OK); - } - - operator sqlite3*() const { return database; } - sqlite3** operator&() { return &database; } -}; - -struct SQLiteStatement { - sqlite3_stmt* stmt = nullptr; - - SQLiteStatement(const SQLiteStatement&) = delete; - SQLiteStatement& operator=(const SQLiteStatement&) = delete; - - SQLiteStatement() = default; - - SQLiteStatement(sqlite3* database, std::string_view sql) { - Initialize(database, sql); - } - - ~SQLiteStatement() { - // NOTE: calling with NULL is a harmless no-op - // NOTE: we don't care about the error code, because they are returned if the statement has errored in the most recent execution - // but deleting it will succeeed anyways - sqlite3_finalize(stmt); - } - - operator sqlite3_stmt*() const { return stmt; } - sqlite3_stmt** operator&() { return &stmt; } - - void Initialize(sqlite3* database, std::string_view sql) { - int result = sqlite3_prepare(database, sql.data(), sql.size(), &stmt, nullptr); - assert(result == SQLITE_OK); - } - - bool InitializeLazily(sqlite3* database, std::string_view sql) { - if (!stmt) { - Initialize(database, sql); - return true; - } - return false; - } -}; - namespace { void PrintErrMsgIfPresent(char*& errMsg) { if (errMsg) { @@ -273,19 +225,12 @@ COMMIT TRANSACTION; return rowId; } - sqlite3_stmt* stmt = storeNamespaceStmt; - int64_t parentRowId = FindOrStoreNamespace(ns->container); - sqlite3_bind_int64(stmt, 1, parentRowId); - sqlite3_bind_text(stmt, 2, ns->name.c_str(), ns->name.size(), nullptr); + SQLiteRunningStatement rt(storeNamespaceStmt); + rt.BindArguments(FindOrStoreNamespace(ns->container), ns->name); - int result = sqlite3_step(stmt); - DEFER { - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - }; - assert(result == SQLITE_ROW); + rt.StepAndCheck(SQLITE_ROW); - auto nsId = sqlite3_column_int64(stmt, 0); + auto [nsId] = rt.ResultColumns<int64_t>(); return nsId; } @@ -299,17 +244,13 @@ COMMIT TRANSACTION; /// \return Row ID of the file, or 0 if it currently doesn't exist. int64_t FindFile(std::string_view filename) { - sqlite3_stmt* stmt = findFileStmt; - sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), nullptr); + SQLiteRunningStatement rt(findFileStmt); + rt.BindArguments(filename); - int result = sqlite3_step(stmt); - DEFER { - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - }; + int result = rt.Step(); if (result == SQLITE_ROW) { - auto fileId = sqlite3_column_int64(stmt, 0); + auto [fileId] = rt.ResultColumns<int64_t>(); return fileId; } else { return 0; @@ -322,17 +263,12 @@ COMMIT TRANSACTION; return id; } - sqlite3_stmt* stmt = storeFileStmt; - sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), nullptr); + SQLiteRunningStatement rt(storeFileStmt); + rt.BindArguments(filename); - int result = sqlite3_step(stmt); - DEFER { - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - }; - assert(result == SQLITE_ROW); + rt.StepAndCheck(SQLITE_ROW); - auto fileId = sqlite3_column_int64(stmt, 0); + auto [fileId] = rt.ResultColumns<int64_t>(); return fileId; } @@ -353,21 +289,13 @@ private: sqlite3_stmt* stmt = getNamespaceStmt; int64_t currentNsId = nsId; while (true) { - sqlite3_bind_int64(stmt, 1, currentNsId); - - int result = sqlite3_step(stmt); - DEFER { - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - }; - assert(result == SQLITE_ROW); + SQLiteRunningStatement rt(getNamespaceStmt); + rt.BindArguments(currentNsId); - /* 0th column Id */ - int64_t parentNamespaceId = sqlite3_column_int64(stmt, 1); - auto nameCstr = (const char*)sqlite3_column_text(stmt, 2); + rt.StepAndCheck(SQLITE_ROW); + auto [id, parentNamespaceId, name] = rt.ResultColumns<int64_t, int64_t, std::string>(); currentNsId = parentNamespaceId; - std::string name(nameCstr); fullnameLength += name.size() + 2; namespaceNames.push_back(std::move(name)); @@ -415,17 +343,12 @@ private: int64_t FindNamespaceImpl(const DeclNamespace& ns, int64_t parentNsRowId) { sqlite3_stmt* stmt = findNamespaceStmt; - sqlite3_bind_int64(stmt, 1, parentNsRowId); - sqlite3_bind_text(stmt, 2, ns.name.c_str(), ns.name.size(), nullptr); - - int result = sqlite3_step(stmt); - DEFER { - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - }; + SQLiteRunningStatement rt(findNamespaceStmt); + rt.BindArguments(parentNsRowId, ns.name); + int result = rt.Step(); if (result == SQLITE_ROW) { - int64_t nsId = sqlite3_column_int64(stmt, 0); + auto [nsId] = rt.ResultColumns<int64_t>(); return nsId; } else { return 0; @@ -584,11 +507,9 @@ void CodegenArchiveModel::DeleteDeclsRelatedToFile(std::string_view filename) { m->deleteEnumDeclByFilenameStmt.stmt, }; for (auto& stmt : stmtList) { - sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), nullptr); - int result = sqlite3_step(stmt); - assert(result == SQLITE_DONE); - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); + SQLiteRunningStatement rt(stmt); + rt.BindArguments(filename); + rt.StepUntilDone(); } m->CommitTransaction(); } @@ -638,91 +559,67 @@ CodegenRuntimeModel CodegenArchiveModel::Load() const { robin_hood::unordered_map<int64_t, DeclMemberVariable*> propertiesById; robin_hood::unordered_map<int64_t, DeclMemberFunction*> methodsById; - // NOTE: this is basically { initializer; while(true) { body; } } in a short form - // Load structs - for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructs"sv);;) { - int result = sqlite3_step(stmt); - if (result == SQLITE_DONE) { - break; - } + { // Load structs + SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructs"sv); + SQLiteRunningStatement rt(stmt); + while (true) { + int result = rt.StepAndCheckError(); + if (result == SQLITE_DONE) break; + assert(result == SQLITE_ROW); - if (result == SQLITE_ROW) { - int64_t id = sqlite3_column_int64(stmt, 9); - int64_t fileId = sqlite3_column_int64(stmt, 1); - int64_t nsId = sqlite3_column_int64(stmt, 2); - std::string_view name((const char*)sqlite3_column_text(stmt, 3)); + auto [id, fileId, nsId, name] = rt.ResultColumns<int64_t, int64_t, int64_t, std::string_view>(); auto decl = cgModel.AddStruct(m->GetDeclFullName(nsId, name), DeclStruct{}); structsById.try_emplace(id, decl); - - continue; } - - // Executing the statement resulted in an error - assert(false); } - // Load struct's base classes - for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructBaseClassRelations");;) { - int result = sqlite3_step(stmt); - if (result == SQLITE_DONE) { - break; - } + { // Load struct's base classes + SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructBaseClassRelations"); + SQLiteRunningStatement rt(stmt); + while (true) { + int result = rt.StepAndCheckError(); + if (result == SQLITE_DONE) break; + assert(result == SQLITE_ROW); - if (result == SQLITE_ROW) { - int64_t structId = sqlite3_column_int64(stmt, 0); - int64_t parentStructNsId = sqlite3_column_int64(stmt, 1); - std::string_view parentStructName((const char*)sqlite3_column_text(stmt, 2)); + auto [structId, parentStructNsId, parentStructName] = rt.ResultColumns<int64_t, int64_t, std::string_view>(); auto declThis = structsById.at(structId); auto declParent = cgModel.FindStruct(parentStructName); // TODO namespace declThis->baseClasses.push_back(declParent); - - continue; } - - assert(false); } - // Load struct properties - for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructProperties"sv);;) { - int result = sqlite3_step(stmt); - if (result == SQLITE_DONE) { - break; - } + { // Load struct properties + SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructProperties"sv); + SQLiteRunningStatement rt(stmt); + while (true) { + int result = rt.StepAndCheckError(); + if (result == SQLITE_DONE) break; + assert(result == SQLITE_ROW); - if (result == SQLITE_ROW) { // TODO - continue; } - - assert(false); } - // Load struct methods - for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethods"sv);;) { - int result = sqlite3_step(stmt); - if (result == SQLITE_DONE) { - break; - } + { // Load struct methods + SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethods"sv); + SQLiteRunningStatement rt(stmt); + while (true) { + int result = rt.StepAndCheckError(); + if (result == SQLITE_DONE) break; + assert(result == SQLITE_ROW); - if (result == SQLITE_ROW) { // TODO - continue; } - - assert(false); } - // Load method params - for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethodParameters"sv);;) { - int result = sqlite3_step(stmt); - if (result == SQLITE_DONE) { - break; - } + { // Load method params + SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethodParameters"sv); + SQLiteRunningStatement rt(stmt); + while (true) { + int result = rt.StepAndCheckError(); + if (result == SQLITE_DONE) break; + assert(result == SQLITE_ROW); - if (result == SQLITE_ROW) { // TODO - continue; } - - assert(false); } return cgModel; @@ -764,40 +661,28 @@ INSERT INTO DeclStructProperties(StructId, Name, Type, GetterName, SetterName, I VALUES (?1, ?2, ?3, ?4, ?5, ?6) )"""sv); - int result; - - sqlite3_bind_int64(m->storeStructStmt, 1, m->FindOrStoreNamespace(decl.container)); - sqlite3_bind_text(m->storeStructStmt, 2, decl.name.c_str(), decl.name.size(), nullptr); - sqlite3_bind_int64(m->storeStructStmt, 3, m->FindOrStoreFile(decl.sourceFile)); - sqlite3_bind_int(m->storeStructStmt, 4, decl.generating); - result = sqlite3_step(m->storeStructStmt); - assert(result == SQLITE_ROW); - int64_t structId = sqlite3_column_int64(m->storeStructStmt, 0); - sqlite3_reset(m->storeStructStmt); - sqlite3_clear_bindings(m->storeStructStmt); + SQLiteRunningStatement rt(m->storeStructStmt); + rt.BindArguments(m->FindOrStoreNamespace(decl.container), decl.name, m->FindOrStoreFile(decl.sourceFile), decl.generating); + rt.StepAndCheck(SQLITE_ROW); + auto [structId] = rt.ResultColumns<int64_t>(); for (auto& baseClass : decl.baseClasses) { - sqlite3_bind_int64(m->storeStructBaseClassStmt, 1, structId); - sqlite3_bind_int64(m->storeStructBaseClassStmt, 2, m->FindOrStoreNamespace(baseClass->container)); - sqlite3_bind_text(m->storeStructBaseClassStmt, 3, baseClass->name.c_str(), baseClass->name.size(), nullptr); - int result = sqlite3_step(m->storeStructBaseClassStmt); - assert(result == SQLITE_DONE); - sqlite3_reset(m->storeStructBaseClassStmt); - sqlite3_clear_bindings(m->storeStructBaseClassStmt); + SQLiteRunningStatement rt(m->storeStructBaseClassStmt); + rt.BindArguments(structId, m->FindOrStoreNamespace(baseClass->container), baseClass->name); + rt.StepUntilDone(); } for (auto& property : decl.memberVariables) { - sqlite3_bind_int64(m->storeStructPropertyStmt, 1, structId); - sqlite3_bind_text(m->storeStructPropertyStmt, 2, property.name.c_str(), property.name.size(), nullptr); - sqlite3_bind_text(m->storeStructPropertyStmt, 3, property.type.c_str(), property.type.size(), nullptr); - sqlite3_bind_text(m->storeStructPropertyStmt, 4, property.getterName.c_str(), property.getterName.size(), nullptr); - sqlite3_bind_text(m->storeStructPropertyStmt, 5, property.setterName.c_str(), property.setterName.size(), nullptr); - // Since DeclMemberVariable entries currently only exist if it's marked BRUSSEL_PROPERTY - sqlite3_bind_int(m->storeStructPropertyStmt, 6, true); - int result = sqlite3_step(m->storeStructPropertyStmt); - assert(result == SQLITE_DONE); - sqlite3_reset(m->storeStructPropertyStmt); - sqlite3_clear_bindings(m->storeStructPropertyStmt); + SQLiteRunningStatement rt(m->storeStructPropertyStmt); + rt.BindArguments( + structId, + property.name, + property.type, + property.getterName, + property.setterName, + // Since DeclMemberVariable entries currently only exist if it's marked BRUSSEL_PROPERTY + true); + rt.StepUntilDone(); } for (auto& method : decl.memberFunctions) { @@ -834,24 +719,14 @@ VALUES (?1, ?2, ?3) ON CONFLICT DO UPDATE SET Value=?3 )"""sv); - sqlite3_bind_int(m->storeEnumStmt, 1, m->FindOrStoreNamespace(decl.container)); - sqlite3_bind_text(m->storeEnumStmt, 2, decl.name.c_str(), decl.name.size(), nullptr); - auto eutName = decl.GetUnderlyingTypeName(); - sqlite3_bind_text(m->storeEnumStmt, 3, eutName.data(), eutName.size(), nullptr); - sqlite3_bind_int64(m->storeEnumStmt, 4, m->FindOrStoreFile(decl.sourceFile)); - int result = sqlite3_step(m->storeEnumStmt); - assert(result == SQLITE_ROW); - auto enumId = sqlite3_column_int64(m->storeEnumStmt, 0); - sqlite3_reset(m->storeEnumStmt); - sqlite3_clear_bindings(m->storeEnumStmt); + SQLiteRunningStatement rt(m->storeEnumStmt); + rt.BindArguments(m->FindOrStoreNamespace(decl.container), decl.name, decl.GetUnderlyingTypeName(), m->FindOrStoreFile(decl.sourceFile)); + rt.StepAndCheck(SQLITE_ROW); + auto [enumId] = rt.ResultColumns<int64_t>(); for (auto& elm : decl.elements) { - sqlite3_bind_int64(m->storeEnumElmStmt, 1, enumId); - sqlite3_bind_text(m->storeEnumElmStmt, 2, elm.name.c_str(), elm.name.size(), nullptr); - sqlite3_bind_int64(m->storeEnumElmStmt, 3, elm.value); - int result = sqlite3_step(m->storeEnumElmStmt); - assert(result == SQLITE_DONE); - sqlite3_reset(m->storeEnumElmStmt); - sqlite3_clear_bindings(m->storeEnumElmStmt); + SQLiteRunningStatement rt(m->storeEnumElmStmt); + rt.BindArguments(enumId, elm.name, elm.value); + rt.StepUntilDone(); } } diff --git a/source/20-codegen-compiler/SQLiteHelper.hpp b/source/20-codegen-compiler/SQLiteHelper.hpp new file mode 100644 index 0000000..c33c2a3 --- /dev/null +++ b/source/20-codegen-compiler/SQLiteHelper.hpp @@ -0,0 +1,204 @@ +#pragma once + +#include <fmt/format.h> +#include <sqlite3.h> +#include <cassert> +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <sstream> +#include <stdexcept> +#include <string_view> +#include <tuple> +#include <type_traits> + +struct SQLiteDatabase { + sqlite3* database = nullptr; + + ~SQLiteDatabase() { + // NOTE: calling with NULL is a harmless no-op + int result = sqlite3_close(database); + assert(result == SQLITE_OK); + } + + operator sqlite3*() const { return database; } + sqlite3** operator&() { return &database; } +}; + +struct SQLiteStatement { + sqlite3_stmt* stmt = nullptr; + + SQLiteStatement(const SQLiteStatement&) = delete; + SQLiteStatement& operator=(const SQLiteStatement&) = delete; + + SQLiteStatement() = default; + + SQLiteStatement(sqlite3* database, std::string_view sql) { + Initialize(database, sql); + } + + ~SQLiteStatement() { + // NOTE: calling with NULL is a harmless no-op + // NOTE: we don't care about the error code, because they are returned if the statement has errored in the most recent execution + // but deleting it will succeeed anyways + sqlite3_finalize(stmt); + } + + operator sqlite3_stmt*() const { return stmt; } + sqlite3_stmt** operator&() { return &stmt; } + + void Initialize(sqlite3* database, std::string_view sql) { + int result = sqlite3_prepare_v2(database, sql.data(), sql.size(), &stmt, nullptr); + if (result != SQLITE_OK) { + auto msg = fmt::format( + "Failed to prepare SQLite3 statement, error message: {}", + sqlite3_errmsg(sqlite3_db_handle(stmt))); + throw std::runtime_error(msg); + } + } + + bool InitializeLazily(sqlite3* database, std::string_view sql) { + if (!stmt) { + Initialize(database, sql); + return true; + } + return false; + } +}; + +struct SQLiteRunningStatement { + sqlite3_stmt* stmt; + + SQLiteRunningStatement(sqlite3_stmt* stmt) + : stmt{ stmt } { + } + + SQLiteRunningStatement(const SQLiteStatement& stmt) + : stmt{ stmt.stmt } { + } + + ~SQLiteRunningStatement() { + sqlite3_clear_bindings(stmt); + sqlite3_reset(stmt); + } + + void BindArgument(int index, int32_t value) { + sqlite3_bind_int(stmt, index, (int)value); + } + + void BindArgument(int index, uint32_t value) { + sqlite3_bind_int(stmt, index, (int)value); + } + + void BindArgument(int index, int64_t value) { + sqlite3_bind_int64(stmt, index, value); + } + + void BindArgument(int index, uint64_t value) { + sqlite3_bind_int64(stmt, index, (int64_t)value); + } + + void BindArgument(int index, const char* value) { + sqlite3_bind_text(stmt, index, value, -1, nullptr); + } + + void BindArgument(int index, std::string_view value) { + sqlite3_bind_text(stmt, index, value.data(), value.size(), nullptr); + } + + void BindArgument(int index, std::nullptr_t) { + // Noop + } + + template <typename... Ts> + void BindArguments(Ts... args) { + // NOTE: SQLite3 argument index starts at 1 + size_t idx = 1; + auto HandleEachArgument = [this, &idx](auto arg) { + BindArgument(idx, arg); + ++idx; + }; + (HandleEachArgument(std::forward<Ts>(args)), ...); + } + + int Step() { + return sqlite3_step(stmt); + } + + void StepAndCheck(int forErr) { + int err = sqlite3_step(stmt); + assert(err == forErr); + } + + int StepAndCheckError() { + int err = sqlite3_step(stmt); + if (err != SQLITE_DONE || err != SQLITE_ROW) { + auto msg = fmt::format( + "Error {} executing SQLite3 statement, error message: {}", + sqlite3_errstr(err), + sqlite3_errmsg(sqlite3_db_handle(stmt))); + throw std::runtime_error(msg); + } + } + + void StepUntilDone() { + while (true) { + int err = sqlite3_step(stmt); + // SQLITE_OK is never returned for sqlite3_step() //TODO fact check this + if (err == SQLITE_DONE) { + break; + } + if (err == SQLITE_ROW) { + continue; + } + + auto msg = fmt::format( + "Error {} executing SQLite3 statement, error message: {}", + sqlite3_errstr(err), + sqlite3_errmsg(sqlite3_db_handle(stmt))); + throw std::runtime_error(msg); + } + } + + using TimePoint = std::chrono::time_point<std::chrono::system_clock>; + using TpFromUnixTimestamp = std::pair<TimePoint, int64_t>; + using TpFromDateTime = std::pair<TimePoint, const char*>; + + // TODO replace with overloads? + template <typename T> + auto ResultColumn(int column) const { + if constexpr (std::is_enum_v<T>) { + auto value = sqlite3_column_int64(stmt, column); + return static_cast<T>(value); + } else if constexpr (std::is_same_v<T, int>) { + return sqlite3_column_int(stmt, column); + } else if constexpr (std::is_same_v<T, int64_t>) { + return sqlite3_column_int64(stmt, column); + } else if constexpr (std::is_same_v<T, const char*>) { + return (const char*)sqlite3_column_text(stmt, column); + } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) { + auto cstr = (const char*)sqlite3_column_text(stmt, column); + return T(cstr); + } else if constexpr (std::is_same_v<T, TpFromUnixTimestamp>) { + auto unixTimestamp = sqlite3_column_int64(stmt, column); + auto chrono = std::chrono::seconds(unixTimestamp); + return TimePoint(chrono); + } else if constexpr (std::is_same_v<T, TpFromDateTime>) { + // TODO wait for libstdc++ and libc++ implement c++20 std::chrono addition + static_assert(false && sizeof(T), "Unimplemented"); + } else { + static_assert(false && sizeof(T), "Unknown type"); + } + } + + template <typename... Ts> + auto ResultColumns() { + // NOTE: SQLite3 column index starts at 0 + // NOTE: ((size_t)-1) + 1 == 0 + size_t idx = -1; + // NOTE: std::make_tuple() -- variadic template function + // std::tuple() -- CTAD constructor + // Both of these cause make the comma operator unsequenced, not viable here + return std::tuple{ (++idx, ResultColumn<Ts>(idx))... }; + } +}; diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp index 5b82d02..845f2f2 100644 --- a/source/20-codegen-compiler/main.cpp +++ b/source/20-codegen-compiler/main.cpp @@ -229,17 +229,6 @@ TryConsumeDirectiveArgumentList(CodegenLexer& lexer) { return result; } -std::vector<const StbLexerToken*>* -GetDirectiveArgument(std::vector<std::vector<const StbLexerToken*>>& list, size_t idx, const char* errMsg = nullptr) { - if (idx < list.size()) { - if (errMsg) { - printf("%s", errMsg); - } - return &list[idx]; - } - return nullptr; -} - bool TryConsumeKeyword(CodegenLexer& lexer, CppKeyword keyword) { auto& token = lexer.Current(); if (token.type == CLEX_id) { @@ -1360,11 +1349,19 @@ where --output-dir=<path>: the *directory* to write generated contents to. Thi } for (auto decl : as.enumsToRevisit) { + if (!decl->generating) { + continue; + } + auto& headerOutput = decl->sourceFile->postHeaderOutput; auto& sourceOutput = decl->sourceFile->postSourceOutput; GenerateForEnum(headerOutput, sourceOutput, *decl); } for (auto decl : as.structsToRevisit) { + if (!decl->generating) { + continue; + } + auto& headerOutput = decl->sourceFile->postHeaderOutput; auto& sourceOutput = decl->sourceFile->postSourceOutput; |