diff --git a/plugins/java-decompiler/engine/BUILD.bazel b/plugins/java-decompiler/engine/BUILD.bazel
index 2399fcec49ca..f9d72d5cd3fa 100644
--- a/plugins/java-decompiler/engine/BUILD.bazel
+++ b/plugins/java-decompiler/engine/BUILD.bazel
@@ -25,6 +25,7 @@ jvm_library(
"@lib//:junit4",
"@lib//:assert_j",
"@lib//:jetbrains-annotations",
+ "@lib//:hamcrest",
]
)
### auto-generated section `build intellij.java.decompiler.engine` end
diff --git a/plugins/java-decompiler/engine/intellij.java.decompiler.engine.iml b/plugins/java-decompiler/engine/intellij.java.decompiler.engine.iml
index 88c1aeb4a94d..9a68a32888d3 100644
--- a/plugins/java-decompiler/engine/intellij.java.decompiler.engine.iml
+++ b/plugins/java-decompiler/engine/intellij.java.decompiler.engine.iml
@@ -12,5 +12,6 @@
+
\ No newline at end of file
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java
index 26e0b3299c0f..5ff32101d59c 100644
--- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java
@@ -1475,10 +1475,13 @@ public class ClassWriter {
TargetInfo.TypeParameterTarget.extract(typeAnnotations, i).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));
buffer.append(parameters.get(i));
List parameterBounds = bounds.get(i);
- if (parameterBounds.size() > 1 || !"java/lang/Object".equals(parameterBounds.get(0).getValue())) {
+ List firstTypeAnnotations = TargetInfo.TypeParameterBoundTarget.extract(typeAnnotations, i, 0);
+ if (parameterBounds.size() > 1 ||
+ (parameterBounds.size() == 1 && !"java/lang/Object".equals(parameterBounds.getFirst().getValue()) ||
+ !firstTypeAnnotations.isEmpty())) {
buffer.append(" extends ");
- TargetInfo.TypeParameterBoundTarget.extract(typeAnnotations, i, 0).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));
- buffer.append(ExprProcessor.getCastTypeName(parameterBounds.get(0), Collections.emptyList()));
+ firstTypeAnnotations.forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));
+ buffer.append(ExprProcessor.getCastTypeName(parameterBounds.getFirst(), Collections.emptyList()));
for (int j = 1; j < parameterBounds.size(); j++) {
buffer.append(" & ");
TargetInfo.TypeParameterBoundTarget.extract(typeAnnotations, i, j).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));
diff --git a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java
index c4b25e26e5a7..9531ee1e288a 100644
--- a/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java
+++ b/plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java
@@ -264,4 +264,7 @@ public class SingleClassesTest extends SingleClassesTestBase {
"typeAnnotations/A", "typeAnnotations/B", "typeAnnotations/C", "typeAnnotations/D", "typeAnnotations/E",
"typeAnnotations/F", "typeAnnotations/Z", "typeAnnotations/P", "typeAnnotations/S", "typeAnnotations/T");
}
+ @Test public void testAnnotationExtendObjectStaticMethods() { doTest("pkg/TestAnnotationExtendObjectStaticMethods"); }
+ @Test public void testAnnotationExtendObjectClass() { doTest("pkg/TestAnnotationExtendObjectClass"); }
+ @Test public void testAnnotationExtendWildcard() { doTest("pkg/TestAnnotationExtendWildcard"); }
}
\ No newline at end of file
diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendObjectClass.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendObjectClass.class
new file mode 100644
index 000000000000..bdfeed33e80c
Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendObjectClass.class differ
diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendObjectStaticMethods.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendObjectStaticMethods.class
new file mode 100644
index 000000000000..155d8a986dc1
Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendObjectStaticMethods.class differ
diff --git a/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendWildcard.class b/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendWildcard.class
new file mode 100644
index 000000000000..ef9f74054a2c
Binary files /dev/null and b/plugins/java-decompiler/engine/testData/classes/pkg/TestAnnotationExtendWildcard.class differ
diff --git a/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendObjectClass.dec b/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendObjectClass.dec
new file mode 100644
index 000000000000..d4ce5ddf01b9
--- /dev/null
+++ b/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendObjectClass.dec
@@ -0,0 +1,17 @@
+package org.example;
+
+import org.jetbrains.annotations.Nullable;
+
+public class TestAnnotationExtendObjectClass {
+ public static void main(String[] args) {
+ }// 8
+}
+
+class 'org/example/TestAnnotationExtendObjectClass' {
+ method 'main ([Ljava/lang/String;)V' {
+ 0 6
+ }
+}
+
+Lines mapping:
+8 <-> 7
diff --git a/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendObjectStaticMethods.dec b/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendObjectStaticMethods.dec
new file mode 100644
index 000000000000..33449ea64e8f
--- /dev/null
+++ b/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendObjectStaticMethods.dec
@@ -0,0 +1,25 @@
+package org.example;
+
+import org.jetbrains.annotations.Nullable;
+
+public class TestAnnotationExtendObjectStaticMethods {
+ public static void main(String[] args) {
+ }// 8
+
+ public static native Iterable concat(Iterable extends T> var0, Iterable extends T> var1);
+
+ public static native Iterable concat2(Iterable extends T> var0, Iterable extends T> var1);
+
+ public static native Iterable concat3(Iterable extends T> var0, Iterable extends T> var1);
+
+ public static native Iterable concat4(Iterable extends T> var0, Iterable extends T> var1);
+}
+
+class 'org/example/TestAnnotationExtendObjectStaticMethods' {
+ method 'main ([Ljava/lang/String;)V' {
+ 0 6
+ }
+}
+
+Lines mapping:
+8 <-> 7
diff --git a/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendWildcard.dec b/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendWildcard.dec
new file mode 100644
index 000000000000..dfd352f06a45
--- /dev/null
+++ b/plugins/java-decompiler/engine/testData/results/TestAnnotationExtendWildcard.dec
@@ -0,0 +1,68 @@
+package org.example;
+
+import java.util.List;
+import org.jetbrains.annotations.Nullable;
+
+public class TestAnnotationExtendWildcard {
+ public static void main(String[] args) {
+ }// 10
+
+ List extends Object> getA() {
+ return null;// 13
+ }
+
+ List> getA5() {
+ return null;// 17
+ }
+
+ List extends String> getA3() {
+ return null;// 21
+ }
+
+ List extends @Nullable Object> getA2() {
+ return null;// 25
+ }
+
+ List extends @Nullable String> getA4() {
+ return null;// 29
+ }
+}
+
+class 'org/example/TestAnnotationExtendWildcard' {
+ method 'main ([Ljava/lang/String;)V' {
+ 0 7
+ }
+
+ method 'getA ()Ljava/util/List;' {
+ 0 10
+ 1 10
+ }
+
+ method 'getA5 ()Ljava/util/List;' {
+ 0 14
+ 1 14
+ }
+
+ method 'getA3 ()Ljava/util/List;' {
+ 0 18
+ 1 18
+ }
+
+ method 'getA2 ()Ljava/util/List;' {
+ 0 22
+ 1 22
+ }
+
+ method 'getA4 ()Ljava/util/List;' {
+ 0 26
+ 1 26
+ }
+}
+
+Lines mapping:
+10 <-> 8
+13 <-> 11
+17 <-> 15
+21 <-> 19
+25 <-> 23
+29 <-> 27
diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendObjectClass.java b/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendObjectClass.java
new file mode 100644
index 000000000000..e8aed5aac839
--- /dev/null
+++ b/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendObjectClass.java
@@ -0,0 +1,9 @@
+package org.example;
+
+import org.jetbrains.annotations.Nullable;
+
+public class TestAnnotationExtendObjectClass {
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendObjectStaticMethods.java b/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendObjectStaticMethods.java
new file mode 100644
index 000000000000..1f4f03533634
--- /dev/null
+++ b/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendObjectStaticMethods.java
@@ -0,0 +1,13 @@
+package org.example;
+
+import org.jetbrains.annotations.Nullable;
+
+public class TestAnnotationExtendObjectStaticMethods {
+ public static void main(String[] args) {
+
+ }
+ public static native Iterable concat(Iterable extends T> a, Iterable extends T> b);
+ public static native Iterable concat2(Iterable extends T> a, Iterable extends T> b);
+ public static native Iterable concat3(Iterable extends T> a, Iterable extends T> b);
+ public static native Iterable concat4(Iterable extends T> a, Iterable extends T> b);
+}
\ No newline at end of file
diff --git a/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendWildcard.java b/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendWildcard.java
new file mode 100644
index 000000000000..ed44f811f81e
--- /dev/null
+++ b/plugins/java-decompiler/engine/testData/src/pkg/TestAnnotationExtendWildcard.java
@@ -0,0 +1,31 @@
+package org.example;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class TestAnnotationExtendWildcard {
+ public static void main(String[] args) {
+
+ }
+
+ List extends Object> getA() {
+ return null;
+ }
+
+ List> getA5() {
+ return null;
+ }
+
+ List extends String> getA3() {
+ return null;
+ }
+
+ List extends @Nullable Object> getA2() {
+ return null;
+ }
+
+ List extends @Nullable String> getA4() {
+ return null;
+ }
+}
\ No newline at end of file