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 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 committed 2026-10-20 15:14:02 +00:00
1 parent c2858d2aa0
commit ffe4441522
3 files changed
+53 -3

No files matched your search

+14 -1
View File
@@ -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] ??
+37 -1
View File
@@ -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 });
});
});
+2 -1
View File
@@ -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"),
),