From 08169eae25f39d0977eb01e6e7e596501c9d61a3 Mon Sep 17 00:00:00 2001 From: GalaxyGamingFR <153150716+GalaxyGamingFR@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:03:50 -0700 Subject: [PATCH] fix(tools): ignore wrapped declarations in pr_overlap exports exports() only checked whether the matching line ended with a semicolon, so a prototype wrapped over several lines was counted as an implemented export and two PRs adding the same forward declaration were flagged as overlapping. It now joins the following added lines up to the first semicolon or brace and treats the match as a declaration when the semicolon comes first. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq --- CMakeLists.txt | 2 ++ tools/pr_overlap.py | 13 +++++++-- .../tests/test_pr_overlap_wrapped_exports.py | 28 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tools/tests/test_pr_overlap_wrapped_exports.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cb4fb85c..53577c535 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/tools/hw-oracle/test_hw_oracle.py) add_test(NAME pr_overlap_exports COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_exports.py) + add_test(NAME pr_overlap_wrapped_exports COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_wrapped_exports.py) add_test(NAME nid_database_cache COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_nid_names.py) endif() diff --git a/tools/pr_overlap.py b/tools/pr_overlap.py index e84ee058d..82aa9b527 100644 --- a/tools/pr_overlap.py +++ b/tools/pr_overlap.py @@ -70,9 +70,18 @@ def commit(tree): def exports(diff): found = set() - for line in diff.splitlines(): + lines = diff.splitlines() + for i, line in enumerate(lines): m = EXPORT.match(line) - if m and not m.group(1).endswith("_nid_no_patch") and not line.rstrip().endswith(";"): + if not m or m.group(1).endswith("_nid_no_patch"): + continue + text = line[1:] + for nxt in lines[i + 1:]: + if ";" in text or "{" in text or not nxt.startswith("+") or nxt.startswith("+++"): + break + text += nxt[1:] + semi, brace = text.find(";"), text.find("{") + if semi < 0 or 0 <= brace < semi: found.add(m.group(1)) return found diff --git a/tools/tests/test_pr_overlap_wrapped_exports.py b/tools/tests/test_pr_overlap_wrapped_exports.py new file mode 100644 index 000000000..96dbd2232 --- /dev/null +++ b/tools/tests/test_pr_overlap_wrapped_exports.py @@ -0,0 +1,28 @@ +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +import pr_overlap + +DECL = "+int APS5_VABI sceFooBar(int a,\n+ int b);\n" + + +class WrappedExportTests(unittest.TestCase): + def test_wrapped_declaration_is_not_export(self): + self.assertEqual(pr_overlap.exports(DECL), set()) + + def test_wrapped_definition_is_export(self): + diff = "+int APS5_VABI sceFooBar(int a,\n+ int b) {\n+ return a + b;\n+}\n" + self.assertEqual(pr_overlap.exports(diff), {"sceFooBar"}) + + def test_declaration_then_definition_returns_only_definition(self): + diff = DECL + "+int APS5_VABI sceBaz(int a) { return a; }\n" + self.assertEqual(pr_overlap.exports(diff), {"sceBaz"}) + + def test_declaration_at_end_of_diff(self): + self.assertEqual(pr_overlap.exports("+int APS5_VABI sceFooBar(int a, int b);"), set()) + + +if __name__ == "__main__": + unittest.main()