From ffe444152293698202166b522e5ecfe9b6064270 Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:14:02 +0000 Subject: [PATCH] fix(swift): match legacy runtime names by format, not a _Tt substring analyze_swift_types treated any symbol containing `_Tt` as a Swift observation, and get_objc_protocols treated any symbol containing `_TtP` as a protocol, so Objective-C method labels and message-send shims for selectors such as `_TtReset` or `_TtPing` were reported as Swift names or protocols. Recognize an embedded legacy runtime name only when `_Tt` is followed by nominal kind markers and an identifier length or the `s` module, owned by the Swift classifier and reused by protocol discovery. Fixes #5576 Fixes #5580 Co-Authored-By: Claude Opus 5.5 --- src/domain/swiftSymbolClassification.ts | 15 +++++++++- src/domain/symbolAnalysis.test.ts | 38 ++++++++++++++++++++++++- src/domain/symbolAnalysis.ts | 3 +- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/domain/swiftSymbolClassification.ts b/src/domain/swiftSymbolClassification.ts index 04910e5c58..6f4651d174 100644 --- a/src/domain/swiftSymbolClassification.ts +++ b/src/domain/swiftSymbolClassification.ts @@ -6,6 +6,19 @@ const LEGACY_CATEGORIES = [ ["_TtE", "extensions"], ] as const; +/** + * A legacy Swift runtime name (`_TtC5Hello3Foo`, `_TtPs5Error_`, `_TtGC…`) + * embedded in a symbol: `_Tt`, an optional generic `G`, nominal kinds, then a + * decimal identifier length or the standard-library module `s`. Objective-C + * selectors that merely contain `_Tt` (`_TtReset`, `_TtPing`) do not match. + */ +const LEGACY_RUNTIME_NAME = /_TtG?[CVOPE]+(?:[1-9]|s)/u; +const LEGACY_PROTOCOL_NAME = /_TtP(?:[1-9]|s)/u; + +/** Recognize a symbol that embeds a legacy Swift runtime protocol name. */ +export const containsLegacySwiftProtocolName = (name: string): boolean => + LEGACY_PROTOCOL_NAME.test(name); + /** Supported procedure categories derived from Swift symbol encodings. */ export type SwiftTypeCategory = (typeof LEGACY_CATEGORIES)[number][1] | "other"; @@ -25,7 +38,7 @@ export const classifySwiftSymbol = ( ): { readonly category: SwiftTypeCategory | null } | undefined => { const prefix = /^_?(?:\$[sS]|_T0)/u.exec(name); if (prefix === null) { - if (!name.includes("_Tt")) return undefined; + if (!LEGACY_RUNTIME_NAME.test(name)) return undefined; return { category: LEGACY_CATEGORIES.find(([prefix]) => name.startsWith(prefix))?.[1] ?? diff --git a/src/domain/symbolAnalysis.test.ts b/src/domain/symbolAnalysis.test.ts index 995b699875..ef81213b36 100644 --- a/src/domain/symbolAnalysis.test.ts +++ b/src/domain/symbolAnalysis.test.ts @@ -1,6 +1,9 @@ import { describe, expect, it } from "vitest"; -import { categorizeSwiftTypes } from "./symbolAnalysis.js"; +import { + categorizeSwiftTypes, + discoverObjcProtocols, +} from "./symbolAnalysis.js"; describe("modern Swift category evidence", () => { it.each([ @@ -167,3 +170,36 @@ describe("Swift alias ambiguity", () => { }, ); }); + +describe("Objective-C labels that merely contain _Tt", () => { + const labels = (selector: string) => [ + { address: "0x1", name: `-[Widget ${selector}]` }, + { address: "0x2", name: `_objc_msgSend$${selector}` }, + ]; + + it.each(["_TtReset", "_TtPing"])( + "are not Swift observations or protocols: %s", + (selector) => { + expect(categorizeSwiftTypes(labels(selector))).toMatchObject({ + total: 0, + unclassified: [], + }); + expect(discoverObjcProtocols(labels(selector))).toEqual({ + count: 0, + protocols: [], + }); + }, + ); + + it("keeps embedded legacy Swift runtime names", () => { + expect( + discoverObjcProtocols([ + { address: "0x1", name: "_OBJC_PROTOCOL_$__TtP5Hello5Proto_" }, + { address: "0x2", name: "_TtPs5Error_" }, + ]), + ).toMatchObject({ count: 2 }); + expect( + categorizeSwiftTypes([{ address: "0x1", name: "-[_TtC5Hello3Foo bar]" }]), + ).toMatchObject({ total: 1 }); + }); +}); diff --git a/src/domain/symbolAnalysis.ts b/src/domain/symbolAnalysis.ts index 3f0841df45..838b4ebf98 100644 --- a/src/domain/symbolAnalysis.ts +++ b/src/domain/symbolAnalysis.ts @@ -2,6 +2,7 @@ import type { JsonValue } from "./jsonValue.js"; import type { AddressedName } from "./hopperValues.js"; import { classifySwiftSymbol, + containsLegacySwiftProtocolName, isSwiftMangledSymbolName, type SwiftTypeCategory, } from "./swiftSymbolClassification.js"; @@ -48,7 +49,7 @@ export const discoverObjcProtocols = ( names.filter( ({ name }) => name.includes(OBJC_PROTOCOL_MARKER) || - name.includes("_TtP") || + containsLegacySwiftProtocolName(name) || (name.endsWith("Mp") && classifySwiftSymbol(name)?.category === "protocols"), ),