From b5f3ac944b3d8a8031d382fbc774db405edcbf76 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 14 Sep 2026 20:39:32 -0500 Subject: [PATCH] add ability to mark Lua exports as "deprecation" --- library/LuaApi.cpp | 16 ++++++++--- library/LuaWrapper.cpp | 52 ++++++++++++++++++++++++++++++++++++ library/include/DataFuncs.h | 22 +++++++++++---- library/include/LuaWrapper.h | 2 ++ library/include/PluginLua.h | 2 +- 5 files changed, 85 insertions(+), 9 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 92f68d7202..1e14981d06 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1320,11 +1320,21 @@ static void OpenModule(lua_State *state, const char *mname, const luaL_Reg *reg2 lua_pop(state, 1); } -#define WRAPM(module, function) { #function, df::wrap_function(module::function,true) } -#define WRAP(function) { #function, df::wrap_function(function,true) } -#define WRAPN(name, function) { #name, df::wrap_function(function,true) } +#define WRAPM(module, function) { #function, df::wrap_function(module::function, #function, true) } +#define WRAP(function) { #function, df::wrap_function(function, #function, true) } +#define WRAPN(name, function) { #name, df::wrap_function(function, #function, true) } #define CWRAP(name, function) { #name, &Lua::CallWithCatchWrapper } +// The _D variants are used to indicate that a wrapped function is deprecated. +// Deprecated functions will still function but will emit a warning to the DFHack console. +// This warning is displayed only once for each distinct appearance in a script (by script path and line number). +// The additional "message" argument is displayed with the warning; it is recommended that this message +// indicate the recommended replacement or substitution. The message must not be empty. + +#define WRAPM_D(module, function, message) { #function, df::wrap_function(module::function, #function, true, message) } +#define WRAP_D(function, message) { #function, df::wrap_function(function, #function, true, message) } +#define WRAPN_D(name, function, message) { #name, df::wrap_function(function, #function, true, message) } + /***** DFHack module *****/ static string getOSType() diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 1a0c5c3bfe..df81ea808e 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -35,6 +35,8 @@ distribution. // must be last due to MS stupidity #include "DataDefs.h" #include "DataIdentity.h" +#include "Debug.h" +#include "HashUtil.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -46,6 +48,11 @@ distribution. using namespace DFHack; using namespace DFHack::LuaWrapper; +namespace DFHack +{ + DBG_DECLARE(lua, deprecation, DebugCategory::LWARNING); +} + /** * Report an error while accessing a field (index = field name). */ @@ -1869,6 +1876,51 @@ void LuaWrapper::AttachDFGlobals(lua_State *state) lua_pop(state, 1); } +namespace +{ + struct deprecation_notification + { + std::string name; + std::string source; + int line; + + bool operator==(const deprecation_notification& other) const = default; + + struct hash + { + std::size_t operator()(const deprecation_notification& n) const noexcept + { + return hash_value(n.name, n.source, n.line); + } + }; + }; + + static std::unordered_set deprecation_notification_history; +} + +void LuaWrapper::notify_deprecated(lua_State* state, std::string_view name, std::string_view message) +{ + lua_Debug ar; + std::optional notify; + + for (int depth = 0; lua_getstack(state, depth, &ar); depth++) + { + if (lua_getinfo(state, "nSl", &ar) && std::strcmp(ar.what, "Lua") == 0) + { + notify = deprecation_notification{std::string(name),ar.short_src,ar.currentline}; + break; + } + } + + if (!notify) return; + + if (!deprecation_notification_history.contains(*notify)) + { + WARN(deprecation).print("Deprecated function {} called from {} at line {}: {}\n", notify->name, notify->source, notify->line, message); + deprecation_notification_history.insert(*notify); + } +} + namespace DFHack { namespace LuaWrapper { struct LuaToken { int reserved; }; diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h index ab9bb78a26..e09e97df26 100644 --- a/library/include/DataFuncs.h +++ b/library/include/DataFuncs.h @@ -174,21 +174,33 @@ namespace df { template class function_identity : public function_identity_base { T ptr; + const std::string name; + const bool depr; + const std::string depr_message; public: using wrapper = function_wrapper; - function_identity(T ptr, bool vararg) - : function_identity_base(wrapper::num_args, vararg), ptr(ptr) {}; + function_identity(T ptr, std::string_view name, bool vararg, std::string_view depr = {}) + : function_identity_base(wrapper::num_args, vararg), ptr(ptr), name(name), depr(!depr.empty()), depr_message(depr) + {}; - virtual void invoke(lua_State *state, int base) const { wrapper::execute(state, base, ptr); } + virtual void invoke(lua_State *state, int base) const + { + if (depr) + { + DFHack::LuaWrapper::notify_deprecated(state, name, depr_message); + } + wrapper::execute(state, base, ptr); + } }; template - inline function_identity_base *wrap_function(T ptr, bool vararg = false) { + inline function_identity_base* wrap_function(T ptr, std::string_view name = {}, bool vararg = false, std::string_view depr = {}) + { using RT = return_type::type; if constexpr (isPrimitive) - return new function_identity(ptr, vararg); + return new function_identity(ptr, name, vararg, depr); else return nullptr; }} diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index 70f36d3f95..258d4d254a 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -237,4 +237,6 @@ namespace LuaWrapper { void IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct); void AttachDFGlobals(lua_State *state); + + DFHACK_EXPORT void notify_deprecated(lua_State* state, std::string_view name, std::string_view message); }} diff --git a/library/include/PluginLua.h b/library/include/PluginLua.h index 16d447b7bf..f04262c4d7 100644 --- a/library/include/PluginLua.h +++ b/library/include/PluginLua.h @@ -14,6 +14,6 @@ DFhackCExport const DFHack::EventReg plugin_lua_events[] = #define DFHACK_LUA_COMMAND(name) { #name, name } -#define DFHACK_LUA_FUNCTION(name) { #name, df::wrap_function(name,true) } +#define DFHACK_LUA_FUNCTION(name) { #name, df::wrap_function(name,#name,true) } #define DFHACK_LUA_EVENT(name) { #name, &name##_event } #define DFHACK_LUA_END { NULL, NULL }