Skip to content

Use Swift-identifier escaping for file-derived bridge function names - #263

Merged
marcprux merged 1 commit into
skiptools:mainfrom
vincentborko:pr/escape-filename-swift-identifier
Jul 24, 2026
Merged

Use Swift-identifier escaping for file-derived bridge function names#263
marcprux merged 1 commit into
skiptools:mainfrom
vincentborko:pr/escape-filename-swift-identifier

Conversation

@vincentborko

Copy link
Copy Markdown
Contributor

Motivation

Follow-up to #262 (which closed #63). That change escaped the Swift @_cdecl function name derived from a source file name via cdeclEscaped — but cdeclEscaped is a JNI-symbol escaper, not a Swift-identifier escaper. The two have different rules, and reusing the JNI one leaves two cases wrong:

  • . is still broken. cdeclEscaped preserves . (it treats it as a separator that callers replace), but . is invalid in a Swift identifier. A file like Model.generated.swift (a common SwiftGen/Sourcery pattern) still produces an uncompilable declaration:

    func Model.generatedKt_Swift_i(...)   // '.' is not valid in an identifier
  • _ is needlessly mangled. _ is valid in a Swift identifier, but cdeclEscaped mangles it to _1. So Model_Extensions.swift yields Model_1ExtensionsKt_Swift_i on the Swift side, where a plain Model_ExtensionsKt_Swift_i is correct and cleaner.

Fix

Add a dedicated swiftIdentifierEscaped that leaves ASCII alphanumerics and _ untouched and hex-escapes everything else, and apply it to the Swift function name only. The JNI symbol keeps cdeclEscaped (unchanged). The + case from #262 is unaffected — both escapers map + to _0002b, so the existing test passes as-is.

Also corrects the cdeclEscaped doc comment, which claimed . and / -> _ while the code actually preserves ..

Bridging decision

No bridging-surface change. Both escapers are pure string transforms; the JNI symbol (linkage) is untouched, only the Swift-side identifier derivation is corrected.

Testing

  • swift test (native XCTest + transpiled) — full SkipSyntax suite green (925 tests).
  • Existing + test (testBridgeFunctionNameEscapesInvalidFileNameCharacters) unchanged and passing.
  • Added testBridgeFunctionNamePreservesUnderscoreInFileName, which pins the _ case: the Swift function name keeps _, while the JNI symbol still mangles it to _1. Verified the test genuinely pins the fix by reverting typeName to cdeclEscaped — the test then fails with Model_1ExtensionsKt_Swift_i.

@cla-bot cla-bot Bot added the cla-signed label Jul 24, 2026
self.compactMap { ch -> String in
if ch == "_" || (ch.isASCII && (ch.isLetter || ch.isNumber)) {
return String(ch)
} else if let utf16 = ch.utf16.first {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Don't we need to translate all the codepoints instead of just the first one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yea I think you're right let me see

Follow-up to skiptools#262. That change escaped the Swift `@_cdecl` function name
derived from a source file name via `cdeclEscaped`, but `cdeclEscaped` is
a JNI-symbol escaper, not a Swift-identifier escaper — the two have
different rules, so reusing it leaves two cases wrong:

- `.` is preserved by `cdeclEscaped` (it is a separator callers handle),
  but a `.` is invalid in a Swift identifier. A file such as
  `Model.generated.swift` still produces an uncompilable declaration:

      func Model.generatedKt_Swift_i(...)   // '.' is not valid

- `_` is valid in a Swift identifier but `cdeclEscaped` mangles it to
  `_1`, so `Model_Extensions.swift` needlessly yields
  `Model_1ExtensionsKt_Swift_i` on the Swift side.

Add a dedicated `swiftIdentifierEscaped` that leaves ASCII alphanumerics
and `_` untouched and hex-escapes everything else, and apply it to the
Swift function name only; the JNI symbol keeps `cdeclEscaped`. The `+`
case from skiptools#262 is unchanged (both escapers map `+` to `_0002b`). Also
corrects the `cdeclEscaped` doc comment, which claimed `.` -> `_` while
the code preserves `.`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vincentborko
vincentborko force-pushed the pr/escape-filename-swift-identifier branch from 781dcd8 to 1b80c85 Compare July 24, 2026 15:10
@marcprux
marcprux merged commit 6f0be88 into skiptools:main Jul 24, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Escape invalid characters in file names when generating bridging code

2 participants