From 02dad4429f411df53a193329850324c1c5cb93f4 Mon Sep 17 00:00:00 2001 From: GalaxyGamingFR <153150716+GalaxyGamingFR@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:54:38 -0700 Subject: [PATCH] fix(progress): recognise non-static and noexcept stub helpers The stub helper pattern required a leading static and a plain signature, so exports calling helpers in anonymous namespaces, or helpers with noexcept, a trailing return type or a pointer next to the name, counted as done. The pattern now anchors at line start, makes static optional, skips APS5_VABI exports and control keywords, and accepts those suffixes. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq --- CMakeLists.txt | 2 ++ tools/progress.py | 5 ++- tools/tests/test_progress_wrappers.py | 51 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tools/tests/test_progress_wrappers.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd30784f..4f7da9da0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_encoding.py) add_test(NAME progress_render_sliver COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_render_sliver.py) + add_test(NAME progress_wrappers COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_wrappers.py) add_test(NAME import_audit COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_import_audit.py) add_test(NAME conventions_function_bodies COMMAND ${Python3_EXECUTABLE} diff --git a/tools/progress.py b/tools/progress.py index 5139d1ec4..3a3cdd2ce 100644 --- a/tools/progress.py +++ b/tools/progress.py @@ -16,7 +16,10 @@ STUB = "NotImplemented_nid_no_patch" CALL = re.compile(r"\b(\w+)\s*\(") CMAKE_SHARED = re.compile(r'^\s*include\(\s*"?\$\{CMAKE_CURRENT_SOURCE_DIR\}/\.\./([\w.-]+)/[\w.-]+\.cmake"?\s*\)', re.M) SOURCE_SHARED = re.compile(r'^\s*#\s*include\s*"prx/([\w.-]+)/[\w./-]+\.cpp"', re.M) -STUB_WRAPPER = re.compile(r"\bstatic\s+(?:\[\[noreturn\]\]\s+)?[\w:<>,\s*&]+?\s+(\w+)\s*\([^;{]*\)\s*\{") +STUB_WRAPPER = re.compile( + r"^[ \t]*(?:static\s+)?(?:\[\[noreturn\]\]\s+)?(?:(?!APS5_VABI)[\w:<>,\s*&])+?[\s*&]" + r"(?!(?:if|while|for|switch|catch|return|else)\b)(\w+)\s*\([^;{]*\)\s*" + r"(?:const\s*)?(?:noexcept\s*)?(?:->\s*[\w:<>*&\s]+?)?\{", re.M) NON_CODE = re.compile(r'"(?:\\.|[^"\\\n])*"|\'(?:\\.|[^\'\\\n])*\'|//[^\n]*|/\*.*?\*/', re.S) DISCARD = re.compile(r"\(void\)\s*\w+\s*;") EMPTY_RETURN = re.compile(r"return\s*;") diff --git a/tools/tests/test_progress_wrappers.py b/tools/tests/test_progress_wrappers.py new file mode 100644 index 000000000..356e46aaf --- /dev/null +++ b/tools/tests/test_progress_wrappers.py @@ -0,0 +1,51 @@ +import sys +import tempfile +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +import progress + + +def scan(source): + with tempfile.TemporaryDirectory() as directory: + library = Path(directory) / "libSceExample" + library.mkdir() + (library / "Export.cpp").write_text(source) + return progress.scan_library(library) + + +STUB_BODY = "{ NotImplemented_nid_no_patch(__func__); return 0; }\n" +CALLER = "int APS5_VABI sceExampleCall() { return Helper(); }\n" + + +class WrapperTests(unittest.TestCase): + def assertTodo(self, source): + group = scan(source) + self.assertEqual(group["done_names"], []) + self.assertEqual(group["todo_names"], ["sceExampleCall"]) + + def test_non_static_helper_in_anonymous_namespace(self): + self.assertTodo("namespace {\nint Helper() " + STUB_BODY + "}\n" + CALLER) + + def test_noexcept_helper(self): + self.assertTodo("static int Helper() noexcept " + STUB_BODY + CALLER) + + def test_trailing_return_type_helper(self): + self.assertTodo("static auto Helper() -> int " + STUB_BODY + CALLER) + + def test_pointer_next_to_name_helper(self): + self.assertTodo("static int *Helper() { NotImplemented_nid_no_patch(__func__); return nullptr; }\n" + CALLER) + + def test_static_helper_still_works(self): + self.assertTodo("static int Helper() " + STUB_BODY + CALLER) + + def test_unrelated_helper_stays_done(self): + group = scan("namespace {\nint Other() { return 1; }\nint Helper() " + STUB_BODY + "}\n" + "int APS5_VABI sceExampleCall() { return Other(); }\n") + self.assertEqual(group["done_names"], ["sceExampleCall"]) + self.assertEqual(group["todo_names"], []) + + +if __name__ == "__main__": + unittest.main()