[GH-ISSUE #5553] [Bug] analyze_swift_types misclassifies nested Unicode types and hides class results #3382

Closed
opened 2026-10-31 00:39:46 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 20, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5553

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

With Hopper selected, analyze_swift_types {} reports procedures for a nested Unicode enum and class under their enclosing struct's category. analyze_swift_types {"category":"classes"} then omits the nested class procedures from both categorized results and unclassified.

An independent verifier compiled the fixture below and reproduced this through the built source checkout's MCP stdio server. The unfiltered call succeeded with total: 33, structs.count: 22, other.count: 11, and zero classes or enums. The struct bucket included the nested enum's describe() method and the nested class's allocating initializer and metadata accessor. The class-only call succeeded with total: 0 and 11 unrelated unresolved observations; neither affected class observation appeared in unclassified. These counts describe analyzed procedure names, not unique source types.

Reproduction

  1. Use the built source revision below. Save this self-contained fixture as Nested.swift in a temporary working directory:
public struct Envelope {
  public enum 状態 {
    case ok
    public func describe() -> String { "state" }
  }
  public struct 中身 {
    public var x: Int
    public init(x: Int) { self.x = x }
    public func doubled() -> Int { x * 2 }
  }
  public class 部品 {
    public init() {}
    public func use() -> Int { 1 }
  }
}
public enum 外側 {
  case a
  public func f() -> Int { 0 }
}
@main
struct NestedMain {
  static func main() { print(Envelope.状態.ok.describe()) }
}
  1. Compile it with the installed Swift compiler:
swiftc -O -g -parse-as-library -module-name Nested -o nested Nested.swift
  1. Connect an MCP stdio client to node scripts/rea.mjs mcp from the built checkout. The verified run used separate temporary HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME, XDG_STATE_HOME, and TMPDIR directories.
  2. Call open_binary with {"path":"<absolute path to compiled nested>","provider_id":"hopper"}. Call analyze_swift_types with {}, then with {"category":"classes"}. Inspect structuredContent.normalized_result, especially categories and unclassified.
  3. Run the following independent identity check on the names retained by the unfiltered result:
xcrun swift-demangle --expand '_$s6Nested8EnvelopeV007IhuqBAbO8describeSSyF' '_$s6Nested8EnvelopeV007jvrtDBlCAEycfC' '_$s6Nested8EnvelopeV007jvrtDBlCMa' '_$s6Nested006CyqEDsO1fSiyF'
  1. Call close_binary with {}. In the verified run it succeeded, and a subsequent binary_session {} reported open: false and cleanup_pending: false.

This was observed in one independent MCP session. Repeat frequency was not measured. The class's use() method was absent from this procedure inventory and is not claimed as an affected observation.

Expected behavior

The nested enum and class procedures should appear under their own correct categories, or remain explicitly unresolved in other and unclassified. A class-only filter should retain decoded class observations in classes and unresolved observations in unclassified.

This expectation is grounded in src/contracts/enhancedToolContracts.ts:54-56, whose MCP description promises unresolved observations under category filters, and src/domain/swiftSymbolClassification.ts:19-22, which says unsupported encodings remain unknown. src/domain/symbolAnalysis.test.ts:24-53 exercises the unresolved-under-filter contract. Complete Unicode decoding is not required to preserve that contract.

Impact and scope

The tested input produces incorrect procedure categories and counts. A supported filter silently hides the nested class allocator and metadata accessor without reporting uncertainty. This is a bounded P2 assessment; other fixtures and providers were not tested.

A demonstrated workaround is to retrieve the unfiltered result and pass its retained mangled_names to xcrun swift-demangle --expand, then inspect the inner Enum or Class context. This recovered the identities for this fixture, although REA's built-in categories and counts remain incorrect. No target bytes changed.

REA version

6.7.0, built from source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

macOS 27.0.1 build 26A434, arm64; Apple Swift 6.4, swiftlang-6.4.0.34.1; Hopper Disassembler 6.1.0-demo build 42.6295552. Observations apply to this installed Hopper copy; stock licensed behavior was not tested. Node.js version: NOT_ENOUGH_INFO, the independent verification record did not capture it.

Target and artifact context

Self-built thin arm64 Mach-O Swift executable with nested Unicode enum, struct, and class declarations and a top-level Unicode enum control. Compilation used optimization and debug information as shown above.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The independent verifier observed these names in categories.structs.items from the unfiltered MCP result:

_$s6Nested8EnvelopeV007IhuqBAbO8describeSSyF
_$s6Nested8EnvelopeV007jvrtDBlCAEycfC
_$s6Nested8EnvelopeV007jvrtDBlCMa

The expanded Swift demangler output identifies them as Nested.Envelope.状態.describe() -> Swift.String, Nested.Envelope.部品.__allocating_init() -> Nested.Envelope.部品, and type metadata accessor for Nested.Envelope.部品. Their trees contain kind=Enum or kind=Class beneath the enclosing kind=Structure for Envelope. None appeared in unclassified.

As a control, _$s6Nested006CyqEDsO1fSiyF, demangled as Nested.外側.f() -> Swift.Int, appeared in other and unclassified with reason category_not_decoded.

The existing focused check npm run test:local -- src/domain/symbolAnalysis.test.ts passed all 16 tests in the verifier's checkout; those tests do not cover this nested Unicode case.

Typed outcome or recovery expected

Return correct inner categories or explicit category_not_decoded observations. Preserve affected class observations under a class-only filter, either as classes or as unresolved. The enclosing Envelope metadata accessor _$s6Nested8EnvelopeVMa should remain in structs; the nested Unicode struct may correctly become unresolved if its encoding is unsupported.

Analysis or suspected cause

Source-derived reasoning: src/domain/swiftSymbolClassification.ts:35-55 decodes the enclosing EnvelopeV, then retains structs when identifierEnd at lines 78-93 rejects the next zero-prefixed identifier. src/domain/symbolAnalysis.ts:118-142 treats that category as decoded and excludes it under a class filter without recording uncertainty. The fixture and expanded demangler output independently establish that the inner nominal kinds differ from the retained outer kind.

Additional context

Prepared with AI assistance and independently reproduced by a separate verifier. The submitter checked current upstream source rather than running another Hopper session. No commits after the baseline changed the cited classifier, aggregation, contract, or test files through upstream main eac296a7da3356f1c5342155d635975ddd89cba6.

Related but different: #4296 and PR #4319 concern substituted constrained-extension contexts. This report concerns nested Unicode enum and class contexts and their omission under a class filter. The last known good version is NOT_ENOUGH_INFO; no regression range was measured.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5553 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior With Hopper selected, `analyze_swift_types {}` reports procedures for a nested Unicode enum and class under their enclosing struct's category. `analyze_swift_types {"category":"classes"}` then omits the nested class procedures from both categorized results and `unclassified`. An independent verifier compiled the fixture below and reproduced this through the built source checkout's MCP stdio server. The unfiltered call succeeded with `total: 33`, `structs.count: 22`, `other.count: 11`, and zero classes or enums. The struct bucket included the nested enum's `describe()` method and the nested class's allocating initializer and metadata accessor. The class-only call succeeded with `total: 0` and 11 unrelated unresolved observations; neither affected class observation appeared in `unclassified`. These counts describe analyzed procedure names, not unique source types. ### Reproduction 1. Use the built source revision below. Save this self-contained fixture as `Nested.swift` in a temporary working directory: ```swift public struct Envelope { public enum 状態 { case ok public func describe() -> String { "state" } } public struct 中身 { public var x: Int public init(x: Int) { self.x = x } public func doubled() -> Int { x * 2 } } public class 部品 { public init() {} public func use() -> Int { 1 } } } public enum 外側 { case a public func f() -> Int { 0 } } @main struct NestedMain { static func main() { print(Envelope.状態.ok.describe()) } } ``` 2. Compile it with the installed Swift compiler: ```sh swiftc -O -g -parse-as-library -module-name Nested -o nested Nested.swift ``` 3. Connect an MCP stdio client to `node scripts/rea.mjs mcp` from the built checkout. The verified run used separate temporary HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME, XDG_STATE_HOME, and TMPDIR directories. 4. Call `open_binary` with `{"path":"<absolute path to compiled nested>","provider_id":"hopper"}`. Call `analyze_swift_types` with `{}`, then with `{"category":"classes"}`. Inspect `structuredContent.normalized_result`, especially `categories` and `unclassified`. 5. Run the following independent identity check on the names retained by the unfiltered result: ```sh xcrun swift-demangle --expand '_$s6Nested8EnvelopeV007IhuqBAbO8describeSSyF' '_$s6Nested8EnvelopeV007jvrtDBlCAEycfC' '_$s6Nested8EnvelopeV007jvrtDBlCMa' '_$s6Nested006CyqEDsO1fSiyF' ``` 6. Call `close_binary` with `{}`. In the verified run it succeeded, and a subsequent `binary_session {}` reported `open: false` and `cleanup_pending: false`. This was observed in one independent MCP session. Repeat frequency was not measured. The class's `use()` method was absent from this procedure inventory and is not claimed as an affected observation. ### Expected behavior The nested enum and class procedures should appear under their own correct categories, or remain explicitly unresolved in `other` and `unclassified`. A class-only filter should retain decoded class observations in `classes` and unresolved observations in `unclassified`. This expectation is grounded in `src/contracts/enhancedToolContracts.ts:54-56`, whose MCP description promises unresolved observations under category filters, and `src/domain/swiftSymbolClassification.ts:19-22`, which says unsupported encodings remain unknown. `src/domain/symbolAnalysis.test.ts:24-53` exercises the unresolved-under-filter contract. Complete Unicode decoding is not required to preserve that contract. ### Impact and scope The tested input produces incorrect procedure categories and counts. A supported filter silently hides the nested class allocator and metadata accessor without reporting uncertainty. This is a bounded P2 assessment; other fixtures and providers were not tested. A demonstrated workaround is to retrieve the unfiltered result and pass its retained `mangled_names` to `xcrun swift-demangle --expand`, then inspect the inner Enum or Class context. This recovered the identities for this fixture, although REA's built-in categories and counts remain incorrect. No target bytes changed. ### REA version 6.7.0, built from source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform macOS 27.0.1 build 26A434, arm64; Apple Swift 6.4, swiftlang-6.4.0.34.1; Hopper Disassembler 6.1.0-demo build 42.6295552. Observations apply to this installed Hopper copy; stock licensed behavior was not tested. Node.js version: NOT_ENOUGH_INFO, the independent verification record did not capture it. ### Target and artifact context Self-built thin arm64 Mach-O Swift executable with nested Unicode enum, struct, and class declarations and a top-level Unicode enum control. Compilation used optimization and debug information as shown above. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The independent verifier observed these names in `categories.structs.items` from the unfiltered MCP result: ```text _$s6Nested8EnvelopeV007IhuqBAbO8describeSSyF _$s6Nested8EnvelopeV007jvrtDBlCAEycfC _$s6Nested8EnvelopeV007jvrtDBlCMa ``` The expanded Swift demangler output identifies them as `Nested.Envelope.状態.describe() -> Swift.String`, `Nested.Envelope.部品.__allocating_init() -> Nested.Envelope.部品`, and `type metadata accessor for Nested.Envelope.部品`. Their trees contain `kind=Enum` or `kind=Class` beneath the enclosing `kind=Structure` for `Envelope`. None appeared in `unclassified`. As a control, `_$s6Nested006CyqEDsO1fSiyF`, demangled as `Nested.外側.f() -> Swift.Int`, appeared in `other` and `unclassified` with reason `category_not_decoded`. The existing focused check `npm run test:local -- src/domain/symbolAnalysis.test.ts` passed all 16 tests in the verifier's checkout; those tests do not cover this nested Unicode case. ### Typed outcome or recovery expected Return correct inner categories or explicit `category_not_decoded` observations. Preserve affected class observations under a class-only filter, either as classes or as unresolved. The enclosing `Envelope` metadata accessor `_$s6Nested8EnvelopeVMa` should remain in `structs`; the nested Unicode struct may correctly become unresolved if its encoding is unsupported. ### Analysis or suspected cause Source-derived reasoning: `src/domain/swiftSymbolClassification.ts:35-55` decodes the enclosing `EnvelopeV`, then retains `structs` when `identifierEnd` at lines 78-93 rejects the next zero-prefixed identifier. `src/domain/symbolAnalysis.ts:118-142` treats that category as decoded and excludes it under a class filter without recording uncertainty. The fixture and expanded demangler output independently establish that the inner nominal kinds differ from the retained outer kind. ### Additional context Prepared with AI assistance and independently reproduced by a separate verifier. The submitter checked current upstream source rather than running another Hopper session. No commits after the baseline changed the cited classifier, aggregation, contract, or test files through upstream main `eac296a7da3356f1c5342155d635975ddd89cba6`. Related but different: #4296 and PR #4319 concern substituted constrained-extension contexts. This report concerns nested Unicode enum and class contexts and their omission under a class filter. The last known good version is NOT_ENOUGH_INFO; no regression range was measured.
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with canonical issue #4296 ([Bug] analyze_swift_types puts constrained-extension members under structs) and sibling #5552. Both issues uncover structural misclassification where member procedures and nested types are erroneously collapsed into enclosing struct categories in analyze_swift_types. Cataloged under Cluster BA in consolidation tracker #4330.

<!-- gh-comment-id:6295543660 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with canonical issue #4296 (`[Bug] analyze_swift_types puts constrained-extension members under structs`) and sibling #5552. Both issues uncover structural misclassification where member procedures and nested types are erroneously collapsed into enclosing struct categories in `analyze_swift_types`. Cataloged under Cluster BA in consolidation tracker #4330.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3382
No description provided.