Skip to content

[codex] Fix generated asset catalog symbol detection#98

Open
onevcat wants to merge 1 commit intomasterfrom
codex/fix-generated-asset-catalog-symbol-detection
Open

[codex] Fix generated asset catalog symbol detection#98
onevcat wants to merge 1 commit intomasterfrom
codex/fix-generated-asset-catalog-symbol-detection

Conversation

@onevcat
Copy link
Copy Markdown
Owner

@onevcat onevcat commented Apr 21, 2026

Summary

  • add a fixture-backed regression test for nested asset catalog symbols, ImageResource access, and asset names containing dots
  • parse nested Swift member-access symbols without matching unrelated custom type prefixes
  • map generated asset symbols from real asset catalog paths so folder-organized assets are preserved without regressing existing Objective-C symbol support

Root Cause

FengNiao only matched generated Swift asset symbols against the leaf resource key. That worked for simple symbols like .icFlag, but not for asset catalogs organized in folders where Xcode generates chained symbols such as .Icons.Settings.logo. It also treated dotted asset names like empty.icon as literal dots instead of Xcode-style camelCase boundaries.

Validation

  • swift test --filter FengNiaoKitTests

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes FengNiao’s detection of Xcode-generated Swift asset catalog symbols, especially for folder-organized (nested) asset catalogs and asset names containing dots, and adds regression coverage via fixtures/tests.

Changes:

  • Add a fixture-backed regression test project for nested asset-catalog symbols, ImageResource access, and dotted asset names.
  • Improve Swift member-access parsing to recognize nested member-access patterns and avoid matching custom type prefixes that merely end with asset-type names.
  • Update generated asset symbol mapping to derive Swift symbol chains from real .xcassets paths while keeping Objective-C symbol support.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Tests/Fixtures/GeneratedAssetCatalogSymbol/ViewController.swift Adds Swift fixture source referencing nested and ImageResource symbols.
Tests/Fixtures/GeneratedAssetCatalogSymbol/Assets.xcassets/Contents.json Adds top-level asset catalog metadata for the fixture.
Tests/Fixtures/GeneratedAssetCatalogSymbol/Assets.xcassets/unused.imageset/Contents.json Adds an unused imageset fixture resource.
Tests/Fixtures/GeneratedAssetCatalogSymbol/Assets.xcassets/empty.icon.imageset/Contents.json Adds a dotted-name imageset fixture resource.
Tests/Fixtures/GeneratedAssetCatalogSymbol/Assets.xcassets/Symbols/plug.imageset/Contents.json Adds a nested-folder imageset fixture resource.
Tests/Fixtures/GeneratedAssetCatalogSymbol/Assets.xcassets/Icons/Settings/logo.imageset/Contents.json Adds a deeper nested-folder imageset fixture resource.
Tests/FengNiaoKitTests/StringExtensionsTests.swift Adds tests asserting dot-separated names convert to generated symbol keys correctly.
Tests/FengNiaoKitTests/SearchRuleTests.swift Adds tests for nested Swift member-access symbol parsing and ignoring custom type prefixes.
Tests/FengNiaoKitTests/GeneratedAssetSymbolTests.swift Adds an end-to-end regression test ensuring nested-folder assets aren’t misclassified as unused.
Sources/FengNiaoKit/FileSearchRule.swift Updates Swift member-access search logic to extract nested symbol chains in select syntaxes.
Sources/FengNiaoKit/FengNiao.swift Changes generated-symbol usage detection to map from actual asset catalog paths and subtract used paths from unused results.
Sources/FengNiaoKit/Extensions.swift Treats . as a word boundary in camel-casing and introduces a path-component helper for symbol generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

result.insert(".\(identifier)")
excludedRanges.append(match.range)
}

Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SwiftMemberAccessSearchRule currently extracts full nested symbols only for Type( .a.b.c ) and ImageResource.a.b.c. If nested symbols are used directly via type inference (e.g. let r: ImageResource = .Icons.Settings.logo or passed as resource: .Icons.Settings.logo), the fallback regex will only capture the first component (.Icons) and miss the full chain, which can cause used assets to be reported as unused. Consider adding a dedicated pattern that captures dot-prefixed chains (e.g. .A.B.c) regardless of surrounding call syntax, while keeping the negative-lookbehind to avoid regular property access.

Suggested change
let dotPrefixedNestedPattern = #"(?<![A-Za-z0-9_])\.\s*([A-Za-z0-9_]+(?:\s*\.\s*[A-Za-z0-9_]+)+)"#
let dotPrefixedNestedReg = try! NSRegularExpression(pattern: dotPrefixedNestedPattern, options: [])
let dotPrefixedNestedMatches = dotPrefixedNestedReg.matches(in: content, options: [], range: content.fullRange)
for match in dotPrefixedNestedMatches {
let identifierRange = match.range(at: 1)
guard identifierRange.location != NSNotFound else { continue }
let identifier = nsstring.substring(with: identifierRange)
.replacingOccurrences(of: #"\s*\.\s*"#, with: ".", options: .regularExpression)
result.insert(".\(identifier)")
excludedRanges.append(match.range)
}

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +154
@Test("Swift member access rule applies to nested asset catalog symbols")
func swiftMemberAccessRuleAppliesToNestedAssetCatalogSymbols() {
let searcher = SwiftMemberAccessSearchRule()
let content = """
let image = Image(.Icons.Settings.logo)
let resource = ImageResource.Symbols.plug
let direct = Image(ImageResource.emptyIcon)
"""
let result = searcher.search(in: content)
let expected: Set<String> = [
".Icons.Settings.logo",
".Symbols.plug",
".emptyIcon",
]
#expect(result == expected)
}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new nested-symbol search test only covers Image(.Icons.Settings.logo) and ImageResource.Symbols.plug/Image(ImageResource.emptyIcon). To prevent regressions for the common type-inference form, consider adding cases like let r: ImageResource = .Icons.Settings.logo and/or UIImage(resource: .Icons.Settings.logo) (or the equivalent API used by your generated symbols). These cases currently aren’t exercised and are where the regex can easily fall back to capturing only the first component.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants