diff --git a/tools/guest_pointer_audit.py b/tools/guest_pointer_audit.py index d7a0ec359b..0eb8fa1266 100644 --- a/tools/guest_pointer_audit.py +++ b/tools/guest_pointer_audit.py @@ -103,6 +103,35 @@ def accesses(body, name): return sorted(found) +def splice_lines(text): + lines = [] + pending = "" + joined = 0 + for line in text.replace("\r\n", "\n").split("\n"): + if line.endswith("\\"): + pending += line[:-1] + joined += 1 + continue + lines.append(pending + line + "\n" * joined) + pending = "" + joined = 0 + if joined: + lines.append(pending + "\n" * joined) + return "\n".join(lines) + + +def keep_lines(match): + if match.group("code") is not None: + return match.group() + if match.group("comment") is None: + return "LITERAL" + "\n" * match.group().count("\n") + return re.sub(r"[^\n]", " ", match.group()) + + +def source_code(text): + return progress.CPP_TOKEN.sub(keep_lines, splice_lines(text)) + + def audit_text(text): results = [] for match in progress.DEFINITION.finditer(text): @@ -124,7 +153,7 @@ def audit(libraries): for source in sorted(library.rglob("*.cpp")): if "tests" in source.relative_to(library).parts: continue - text = progress.code(source.read_text(encoding="utf-8", errors="ignore")) + text = source_code(source.read_text(encoding="utf-8", errors="ignore")) for entry in audit_text(text): report.append({"library": library.name, "file": source.relative_to(ROOT).as_posix(), **entry}) return report diff --git a/tools/tests/test_guest_pointer_audit.py b/tools/tests/test_guest_pointer_audit.py index 76ca79b40a..56fe54d046 100644 --- a/tools/tests/test_guest_pointer_audit.py +++ b/tools/tests/test_guest_pointer_audit.py @@ -13,7 +13,7 @@ import progress def findings(source): - return [(entry["function"], entry["parameter"], entry["kinds"]) for entry in guest_pointer_audit.audit_text(progress.code(source))] + return [(entry["function"], entry["parameter"], entry["kinds"]) for entry in guest_pointer_audit.audit_text(guest_pointer_audit.source_code(source))] class AccessTests(unittest.TestCase): @@ -101,15 +101,21 @@ class AccessTests(unittest.TestCase): self.assertEqual(findings(source), [("sceExampleSn", "p", ["library call"]), ("sceExampleS", "p", ["library call"])]) def test_line_is_the_earliest_access_of_any_kind(self): - entry, = guest_pointer_audit.audit_text(progress.code("int APS5_VABI sceExampleEarly(Info* p)\n{\n p[0] = {};\n p->field = 1;\n return 0;\n}\n")) + entry, = guest_pointer_audit.audit_text(guest_pointer_audit.source_code("int APS5_VABI sceExampleEarly(Info* p)\n{\n p[0] = {};\n p->field = 1;\n return 0;\n}\n")) self.assertEqual((entry["line"], entry["kinds"]), (3, ["index", "member"])) def test_comments_and_literals_are_ignored(self): source = "int APS5_VABI sceExampleText(Info* info) { // info->size = 4;\n return Log(\"info->size *info info[0]\"); }\n" self.assertEqual(findings(source), []) + def test_line_counts_blank_spliced_and_commented_lines(self): + source = ("int APS5_VABI sceExampleBlank(int* out)\n{\n\n\n /* one\n two */\n const char* s = R\"(a\nb)\";\n" + " int wide = 1 + \\\n 2;\n\n *out = wide;\n return 0;\n}\n") + entry, = guest_pointer_audit.audit_text(guest_pointer_audit.source_code(source)) + self.assertEqual(entry["line"], 12) + def test_line_points_at_the_first_access(self): - entry, = guest_pointer_audit.audit_text(progress.code("int APS5_VABI sceExampleLine(int* out)\n{\n int a = 0;\n *out = a;\n return 0;\n}\n")) + entry, = guest_pointer_audit.audit_text(guest_pointer_audit.source_code("int APS5_VABI sceExampleLine(int* out)\n{\n int a = 0;\n *out = a;\n return 0;\n}\n")) self.assertEqual(entry["line"], 4)