From 819c996ee37ea6f8ea1d17eab34fc3ebfa66bf44 Mon Sep 17 00:00:00 2001 From: Pierluigi Cifani Date: Wed, 4 Nov 2026 11:57:04 +0100 Subject: [PATCH] jextract: skip prefix and postfix operators by their modifier JavaExtractDecider meant to skip prefix and postfix operators by checking the name token for .prefixOperator or .postfixOperator. In a declaration such as `public static prefix func - (value: Number) -> Number` the name is lexed as a binary operator, and only the `prefix` modifier tells it apart. So the operator was extracted as a binary operator, and in JNI mode the Swift thunk printer stopped with `Binary operator must have exactly 2 arguments`. Check the modifiers instead. Co-Authored-By: Claude Opus 5.5 --- .../JExtractSwiftLib/JavaExtractDecider.swift | 16 +++--- .../JNI/JNIOperatorsTests.swift | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/Sources/JExtractSwiftLib/JavaExtractDecider.swift b/Sources/JExtractSwiftLib/JavaExtractDecider.swift index a64ef4466..fefb7cb1f 100644 --- a/Sources/JExtractSwiftLib/JavaExtractDecider.swift +++ b/Sources/JExtractSwiftLib/JavaExtractDecider.swift @@ -71,15 +71,13 @@ public struct JavaExtractDecider: ExtractDecider { return false } - // Swift operators have no Java mapping - if let fn = decl.as(FunctionDeclSyntax.self) { - switch fn.name.tokenKind { - case .prefixOperator, .postfixOperator: - log.trace("Skip '\(decl.qualifiedNameForDebug)': operators are not supported on Java") - return false - default: - break - } + // Swift prefix and postfix operators have no Java mapping. A declaration's operator name is + // lexed as a binary operator, so only the `prefix` or `postfix` modifier tells them apart. + if let fn = decl.as(FunctionDeclSyntax.self), + fn.modifiers.contains(where: { $0.name.tokenKind == .keyword(.prefix) || $0.name.tokenKind == .keyword(.postfix) }) + { + log.trace("Skip '\(decl.qualifiedNameForDebug)': operators are not supported on Java") + return false } guard let mod = decl.asProtocol((any WithModifiersSyntax).self) else { diff --git a/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift b/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift index 9863d109d..1eb5d4240 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIOperatorsTests.swift @@ -149,4 +149,60 @@ struct JNIOperatorsTests { ] ) } + + let prefixAndPostfixSource = + """ + postfix operator ++ + + public struct Number { + public static prefix func - (value: Number) -> Number { + value + } + + public static postfix func ++ (value: Number) -> Number { + value + } + + public static func + (left: Number, right: Number) -> Number { + Number() + } + } + """ + + @Test + func prefixAndPostfixOperators_areSkipped_javaBindings() throws { + try assertOutput( + input: prefixAndPostfixSource, + .jni, + .java, + expectedChunks: [ + """ + private static native long $plus(long left, long right); + """ + ], + notExpectedChunks: [ + "prefix func", + "postfix func", + ] + ) + } + + @Test + func prefixAndPostfixOperators_areSkipped_swiftThunks() throws { + try assertOutput( + input: prefixAndPostfixSource, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_Number__00024plus__JJ") + """ + ], + notExpectedChunks: [ + "minus", + "++", + ] + ) + } }