From 089434352d475f0e2c8f383dbac640128b06ebe9 Mon Sep 17 00:00:00 2001 From: J2ObjC Team Date: Tue, 30 Jun 2026 11:15:20 -0700 Subject: [PATCH] Support @GenerateObjCCompanion for static @Property methods. When @GenerateObjCCompanion generates companion protocol declarations (@protocol FooCompanion), static methods annotated with @Property should be emitted as instance properties rather than class properties (@property (class, ...)). In TypeDeclarationGenerator, pass whether code is being generated for a Kotlin companion protocol to printPseudoProperty so that the class attribute is omitted when declaring companion protocol properties. Also pass ElementUtil.isStatic(methodElement) to findSetterMethod so setters for static properties are found correctly. PiperOrigin-RevId: 940556527 --- .../j2objc/gen/TypeDeclarationGenerator.java | 9 +++++---- .../j2objc/gen/GenerateObjCCompanionTest.java | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java b/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java index 85e2a70319..79179710a3 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java +++ b/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java @@ -722,7 +722,7 @@ private boolean canPrintPseudoProperty(MethodDeclaration m) { return true; } - private void printPseudoProperty(MethodDeclaration m) { + private void printPseudoProperty(MethodDeclaration m, boolean isKotlinCompanion) { ExecutableElement methodElement = m.getExecutableElement(); String methodName = nameTable.getMethodSelector(methodElement); String propertyName = NameTable.lowercaseFirst(methodName.replaceFirst("get", "")); @@ -738,12 +738,13 @@ private void printPseudoProperty(MethodDeclaration m) { TypeMirror returnType = m.getReturnTypeMirror(); ExecutableElement setter = - ElementUtil.findSetterMethod(propertyName, returnType, declaringClass, false); + ElementUtil.findSetterMethod( + propertyName, returnType, declaringClass, ElementUtil.isStatic(methodElement)); newline(); printf( "@property (%snonatomic, %s, %s%s) %s %s;", - ElementUtil.isStatic(methodElement) ? "class, " : "", + ElementUtil.isStatic(methodElement) && !isKotlinCompanion ? "class, " : "", "getter=" + methodName, setter != null ? "setter=" + nameTable.getMethodSelector(setter) : "readonly", shouldAddNullableAnnotation(methodElement) ? ", nullable" : "", @@ -781,7 +782,7 @@ private void printMethodDeclaration( JavadocGenerator.printDocComment(getBuilder(), m.getJavadoc()); if (canPrintPseudoProperty(m)) { - printPseudoProperty(m); + printPseudoProperty(m, isKotlinCompanion); return; } diff --git a/translator/src/test/java/com/google/devtools/j2objc/gen/GenerateObjCCompanionTest.java b/translator/src/test/java/com/google/devtools/j2objc/gen/GenerateObjCCompanionTest.java index e244cebf13..e682bee231 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/gen/GenerateObjCCompanionTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/gen/GenerateObjCCompanionTest.java @@ -142,5 +142,24 @@ public static void doSomething() {} assertInTranslation(impl, "+ (id)companion {"); assertInTranslation(impl, "return (id)self;"); } + + public void testCompanionPropertiesForPropertyMethods() throws IOException { + String source = + """ + import com.google.j2objc.annotations.Property; + @com.google.j2objc.annotations.GenerateObjCCompanion + public class Foo { + @Property("readonly, nonnull") + public static String getBar() { + return "bar"; + } + } + """; + String header = translateSourceFile(source, "Foo", "Foo.h"); + assertInTranslation(header, "@protocol FooCompanion"); + assertInTranslation(header, "@property (nonatomic, getter=getBar, readonly) NSString * bar;"); + assertInTranslation( + header, "@property (class, nonatomic, getter=getBar, readonly) NSString * bar;"); + } }