implemented stack traces on windows

This commit is contained in:
Eric 2023-06-08 15:16:35 -07:00
parent 75a236c897
commit 95815353cc
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
12 changed files with 211 additions and 40 deletions

56
.efrocachemap generated
View File

@ -4072,26 +4072,26 @@
"build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b",
"build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877",
"build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/76/e2/95ac7cceb6e94fd34acc7c350263",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/54/b3/9c381ed5dda3c399c7b955fa8d50",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/a6/77/a0efaffa066d6b00bc996b56636b",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ee/51/d753711059b5438da91fe1ac03db",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/07/5a/2cc7650c428543f5a33013bfceeb",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/1c/38/2665b9bd9bac14ad73ef4986acd7",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/fc/18/11baef9fa7f78211da59e1396fc9",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/5a/fc/fcf4c33bde143d92cfafbdf16ee3",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/83/02/b567d1fd06d21863ba125fd1ae22",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/fc/64/129a84c24ee5074d3b1a7c0d3440",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ad/d2/87c0898b21b23d8d54a16e44a2fd",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/89/8f/27cd935153e755742f79753ce9a0",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/cb/46/21e3e231e46b9a694747bb060333",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/db/e8/73eb4dc951795c665c946c943ebb",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e1/b6/941774ba089254534ac54612a842",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d0/86/9cb45fca207b9b43db1beee48514",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/21/a0/13b7bb057fc1d351427127f5b001",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/8d/63/657b92f76a646cb7cace9c467c26",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/9c/e0/ef535d0fc11b943eced693b1465d",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/86/f0/6a02c50487ba42a86f4bd173e54c",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/fa/78/a214be11993d36a52192d87ec637",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/37/b3/419b2996dcf20e83d2550512ede5",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/fc/32/72b0b40b06ecca15e7da66180553",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8c/8e/692af6872e7a247793d88d9ebf99",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/1c/c6/59084601f9a9ab70c673a8782c5a",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/f4/44/aee3980f4d679d89145abcdc4146",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/99/6e/bb06e27cf507f1ac597cd88ddf99",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/e1/7b/811ab8937a398e7d40c85a22cc0e",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/1b/73/543b3d40a8ab5ec65f549d575736",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/f0/e1/b305a86d0ee307b497b19eb7ae18",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/14/5e/6e8bfa5870766ab3cb0bc1363fc1",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/5a/24/8779125082977f75de7aeac097ca",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/c5/ab/969f814415316f5a32b21b5ec95e",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/63/7a/fb8c34f54070613f56c5ecdb5d0a",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/df/a6/4406c159af542d841a9a598a50d2",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/17/0b/052ded44e214490afb11370e548c",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/49/1f/f9bf566efdd357185713ee2db3f7",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/a5/6f/3f16a4bc453b29f2934269afe369",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/b6/93/f1daebdde56b97167ac43c6e983c",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/51/7c/833fcba248f23598eb606ec1e366",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/58/55/b6ae6dd4f3615fa87bb170a43233",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/b9/f8/37285d0ced7794a82534d13c33ac",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/c7/cc/0c5f0afbfa0ddabaea8d5838562a",
@ -4108,14 +4108,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/96/2e/1b0ba43fe24fe9cc223a434db647",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/f3/83/581b7df96f6516032a0747b83e15",
"build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/6a/6d/7aaf7617929eeff41bc5a574e6c8",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/20/f0/4e9e3644c559e00816a8621a8987",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/7c/39/5390a4370b1c3a931a84abf6f477",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/f8/ec/e98d729c3d66249a1e11aa26af2d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/62/9c/2c38890b97307365059b162abe51",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/90/81/4d46be8eb3992b016a7c82f5766a",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/18/09/af24159486d0253f900e262d9fd9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/a8/f5/68bb806fdee3a4694352d1e03a60",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/ee/96/8259473f7ed61dfdb8dd8af6f845",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/bf/67/b2e47d165f67487a411c10ec6761",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/68/3f/b02ee9d51503a71abe3fc9a160a8",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/87/9b/63f4ed1cef6e7214b69c06962d2c",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/ee/64/6a17dc184bac9e42613a2228e210",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/a3/7c/607018f0aa7f918a978d8d9719e2",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/14/49/7af3c9043dadbb78e5d4467e471b",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/15/a5/28c86995a7171bd8b5d0291da22f",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/93/f5/0ad008b1910c9a630e47d4077d83",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa",
"src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/75/9f/bcf597b362c9f2480cb348188bdd",

View File

@ -1,4 +1,4 @@
### 1.7.20 (build 21073, api 8, 2023-06-08)
### 1.7.20 (build 21074, api 8, 2023-06-08)
- This seems like a good time for a `refactoring` release in anticipation of
changes coming in 1.8. Basically this means that a lot of things will be
@ -300,6 +300,9 @@
- (build 21070) Fixed an issue where teams series would incorrectly end after 1
round (thanks for the heads up SEBASTIAN2059)
- (build 21072) Fixed a crash drawing a terrain node with no texture set.
- (build 21073) Stack traces are now implemented under windows so should show up
for fatal errors and whatnot. Also fatal error logging now mentions when stack
traces are not available.
### 1.7.19 (build 20997, api 7, 2023-01-19)

View File

@ -28,7 +28,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21073
TARGET_BALLISTICA_BUILD = 21074
TARGET_BALLISTICA_VERSION = '1.7.20'
_g_env_config: EnvConfig | None = None

View File

@ -123,10 +123,10 @@ void CoreFeatureSet::PostInit() {
// We can use it to make error messages/etc. more pretty by stripping out
// all but sub-project paths.
const char* f = __FILE__;
auto* f_end = strstr(
f, "src" BA_DIRSLASH "ballistica" BA_DIRSLASH "app" BA_DIRSLASH "app.cc");
if (!f) {
Log(LogLevel::kWarning, "Unable to calc project dir from __FILE__.");
auto* f_end = strstr(f, "src" BA_DIRSLASH "ballistica" BA_DIRSLASH
"core" BA_DIRSLASH "core.cc");
if (!f_end) {
Log(LogLevel::kWarning, "Unable to calc build source dir from __FILE__.");
} else {
build_src_dir_ = std::string(f).substr(0, f_end - f);
}

View File

@ -1078,7 +1078,7 @@ class PlatformStackTraceExecInfo : public PlatformStackTrace {
}
}
auto copy() const noexcept -> PlatformStackTrace* override {
auto Copy() const noexcept -> PlatformStackTrace* override {
try {
auto s = new PlatformStackTraceExecInfo(*this);

View File

@ -30,7 +30,7 @@ class PlatformStackTrace {
// Should return a copy of itself allocated via new() (or nullptr if not
// possible).
virtual auto copy() const noexcept -> PlatformStackTrace* = 0;
virtual auto Copy() const noexcept -> PlatformStackTrace* = 0;
};
/// This class attempts to abstract away most platform-specific

View File

@ -12,6 +12,11 @@
#include <stdio.h>
#include <sysinfoapi.h>
/* clang-format off */
// Builds fail if this is further up.
#include <dbghelp.h>
/* clang-format on */
#pragma comment(lib, "Rpcrt4.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
@ -20,7 +25,9 @@
#else
#pragma comment(lib, "python311.lib")
#endif
#pragma comment(lib, "DbgHelp.lib")
// GUI Only Stuff.
#if !BA_HEADLESS_BUILD
#pragma comment(lib, "libogg.lib")
#pragma comment(lib, "libvorbis.lib")
@ -39,6 +46,108 @@
namespace ballistica::core {
static const int kTraceMaxStackFrames{256};
static const int kTraceMaxFunctionNameLength{1024};
class WinStackTrace : public PlatformStackTrace {
public:
explicit WinStackTrace(CorePlatformWindows* platform) : platform_{platform} {
number_of_frames_ =
CaptureStackBackTrace(0, kTraceMaxStackFrames, stack_, NULL);
}
// Return a human readable version of the trace (with symbolification if
// available).
auto GetDescription() noexcept -> std::string {
return platform_->GetWinStackTraceDescription(this);
}
// Should return a copy of itself allocated via new() (or nullptr if not
// possible).
auto Copy() const noexcept -> PlatformStackTrace* override {
try {
auto s = new WinStackTrace(*this);
// Vanilla copy constructor should do the right thing here.
assert(s->number_of_frames_ == number_of_frames_
&& memcmp(s->stack_, stack_, sizeof(stack_)) == 0);
return s;
} catch (const std::exception&) {
// If this is failing we're in big trouble anyway.
return nullptr;
}
}
auto number_of_frames() const { return number_of_frames_; }
auto* stack() const { return stack_; }
private:
CorePlatformWindows* platform_;
WORD number_of_frames_{};
void* stack_[kTraceMaxStackFrames];
};
auto CorePlatformWindows::GetWinStackTraceDescription(
WinStackTrace* stack_trace) -> std::string {
try {
std::string out;
// Win docs say this is not thread safe so limit to one at a time.
std::scoped_lock lock(win_stack_mutex_);
// Docs say to do this only once.
if (!win_sym_inited_) {
win_sym_process_ = GetCurrentProcess();
SymInitialize(win_sym_process_, NULL, TRUE);
win_sym_inited_ = true;
}
char buf[sizeof(SYMBOL_INFO)
+ (kTraceMaxFunctionNameLength - 1) * sizeof(TCHAR)];
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buf);
symbol->MaxNameLen = kTraceMaxFunctionNameLength;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
DWORD displacement;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
std::string build_src_dir = g_core ? g_core->build_src_dir() : "";
char linebuf[kTraceMaxFunctionNameLength + 128];
for (int i = 0; i < stack_trace->number_of_frames(); i++) {
DWORD64 address = (DWORD64)(stack_trace->stack()[i]);
SymFromAddr(win_sym_process_, address, NULL, symbol);
if (SymGetLineFromAddr64(win_sym_process_, address, &displacement,
&line)) {
const char* filename = line.FileName;
if (!build_src_dir.empty()
&& !strncmp(filename, build_src_dir.c_str(),
build_src_dir.size())) {
filename += build_src_dir.size();
}
snprintf(linebuf, sizeof(linebuf),
"%-3d %s in %s: line: %lu: address: 0x%p\n", i, symbol->Name,
filename, line.LineNumber,
reinterpret_cast<void*>(symbol->Address));
} else {
snprintf(linebuf, sizeof(linebuf),
"SymGetLineFromAddr64 returned error code %lu.\n",
GetLastError());
snprintf(linebuf, sizeof(linebuf), "%-3d %s, address 0x%p.\n", i,
symbol->Name, reinterpret_cast<void*>(symbol->Address));
}
out += linebuf;
}
return out;
} catch (const std::exception&) {
return "stack-trace construction failed.";
}
}
auto CorePlatformWindows::GetStackTrace() -> PlatformStackTrace* {
return new WinStackTrace(this);
}
// Convert a wide Unicode string to an UTF8 string.
auto CorePlatformWindows::UTF8Encode(const std::wstring& wstr) -> std::string {
if (wstr.empty()) return std::string();
@ -61,10 +170,54 @@ auto CorePlatformWindows::UTF8Decode(const std::string& str) -> std::wstring {
return wstr;
}
#define TRACE_MAX_STACK_FRAMES 256
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
// int printStackTrace() {
// void* stack[TRACE_MAX_STACK_FRAMES];
// if (!win_sym_inited_) {
// win_sym_process_ = GetCurrentProcess();
// SymInitialize(win_sym_process_, NULL, TRUE);
// win_sym_inited_ = true;
// }
// WORD numberOfFrames =
// CaptureStackBackTrace(0, TRACE_MAX_STACK_FRAMES, stack, NULL);
// char buf[sizeof(SYMBOL_INFO)
// + (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)];
// SYMBOL_INFO* symbol = (SYMBOL_INFO*)buf;
// symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
// symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
// DWORD displacement;
// IMAGEHLP_LINE64 line;
// line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
// for (int i = 0; i < numberOfFrames; i++) {
// DWORD64 address = (DWORD64)(stack[i]);
// SymFromAddr(win_sym_process_, address, NULL, symbol);
// if (SymGetLineFromAddr64(win_sym_process_, address, &displacement,
// &line)) {
// printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name,
// line.FileName, line.LineNumber, symbol->Address);
// } else {
// printf("\tSymGetLineFromAddr64 returned error code %lu.\n",
// GetLastError());
// printf("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address);
// }
// }
// return 0;
// }
CorePlatformWindows::CorePlatformWindows() {
// We should be built in unicode mode.
assert(sizeof(TCHAR) == 2);
// printStackTrace();
// auto* testtrace = new WinStackTrace(this);
// printf("WINTRACE:\n%s", testtrace->GetDescription().c_str());
// printf("WOOHOO!\n");
// fflush(stdout);
// Need to init winsock immediately since we use it for
// threading/logging/etc.
{

View File

@ -4,6 +4,7 @@
#define BALLISTICA_CORE_PLATFORM_WINDOWS_CORE_PLATFORM_WINDOWS_H_
#if BA_OSTYPE_WINDOWS
#include <mutex>
#include <string>
#include <vector>
@ -11,6 +12,8 @@
namespace ballistica::core {
class WinStackTrace;
class CorePlatformWindows : public CorePlatform {
public:
CorePlatformWindows();
@ -18,6 +21,7 @@ class CorePlatformWindows : public CorePlatform {
static auto UTF8Encode(const std::wstring& wstr) -> std::string;
static auto UTF8Decode(const std::string& str) -> std::wstring;
auto GetStackTrace() -> PlatformStackTrace* override;
auto GetDeviceV1AccountUUIDPrefix() -> std::string override { return "w"; }
auto GetDeviceUUIDInputs() -> std::list<std::string> override;
auto GenerateUUID() -> std::string override;
@ -53,6 +57,13 @@ class CorePlatformWindows : public CorePlatform {
auto GetPlatformName() -> std::string override;
auto GetSubplatformName() -> std::string override;
bool have_stdin_stdout_ = false;
auto GetWinStackTraceDescription(WinStackTrace* stack_trace) -> std::string;
private:
std::mutex win_stack_mutex_;
bool win_sym_inited_{};
void* win_sym_process_{};
};
} // namespace ballistica::core

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21073;
const int kEngineBuildNumber = 21074;
const char* kEngineVersion = "1.7.20";
auto MonolithicMain(const core::CoreConfig& core_config) -> int {

View File

@ -43,7 +43,7 @@ Exception::Exception(const Exception& other) noexcept {
full_description_ = other.full_description_;
python_type_ = other.python_type_;
if (other.stack_trace_) {
stack_trace_ = other.stack_trace_->copy();
stack_trace_ = other.stack_trace_->Copy();
}
} catch (const std::exception&) {
// Hmmm not sure what we should do if this happens;

View File

@ -18,7 +18,7 @@ class CoreFeatureSet;
}
namespace ballistica::base {
class BaseFeatureSet;
class WidgetMessage;
struct WidgetMessage;
} // namespace ballistica::base
namespace ballistica::ui_v1 {

View File

@ -132,7 +132,11 @@ def _get_namespace_info(lines: list[str], index: int) -> tuple[str, bool]:
if lines[index].startswith('}'):
return name, True
if not (
lines[index].startswith('class ') and lines[index].endswith(';')
(
lines[index].startswith('class ')
or lines[index].startswith('struct ')
)
and lines[index].endswith(';')
):
# Found a non-predeclare statement
return name, False