Fix for KT-15027 J2K: Annotations are set on functions, but not on property accessors
#KT-15027 fixed
This commit is contained in:
committed by
Simon Ogorodnik
parent
5071baf970
commit
73ea0e8460
@@ -37,15 +37,15 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
return nameAsString in annotationsToRemove || nameAsString == Target::class.java.name
|
return nameAsString in annotationsToRemove || nameAsString == Target::class.java.name
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertAnnotations(owner: PsiModifierListOwner): Annotations
|
fun convertAnnotations(owner: PsiModifierListOwner, target: AnnotationUseTarget? = null): Annotations
|
||||||
= convertAnnotationsOnly(owner) + convertModifiersToAnnotations(owner)
|
= convertAnnotationsOnly(owner, target) + convertModifiersToAnnotations(owner, target)
|
||||||
|
|
||||||
private fun convertAnnotationsOnly(owner: PsiModifierListOwner): Annotations {
|
private fun convertAnnotationsOnly(owner: PsiModifierListOwner, target: AnnotationUseTarget?): Annotations {
|
||||||
val modifierList = owner.modifierList
|
val modifierList = owner.modifierList
|
||||||
val annotations = modifierList?.annotations?.filter { it.qualifiedName !in annotationsToRemove }
|
val annotations = modifierList?.annotations?.filter { it.qualifiedName !in annotationsToRemove }
|
||||||
|
|
||||||
var convertedAnnotations: List<Annotation> = if (annotations != null && annotations.isNotEmpty()) {
|
var convertedAnnotations: List<Annotation> = if (annotations != null && annotations.isNotEmpty()) {
|
||||||
val newLines = if (!modifierList!!.isInSingleLine()) {
|
val newLines = if (!modifierList.isInSingleLine()) {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -57,14 +57,14 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
if (child is PsiWhiteSpace) !child.isInSingleLine() else false
|
if (child is PsiWhiteSpace) !child.isInSingleLine() else false
|
||||||
}
|
}
|
||||||
|
|
||||||
annotations.mapNotNull { convertAnnotation(it, newLineAfter = newLines) }
|
annotations.mapNotNull { convertAnnotation(it, newLineAfter = newLines, target = target) }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
listOf()
|
listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner is PsiDocCommentOwner) {
|
if (owner is PsiDocCommentOwner) {
|
||||||
val deprecatedAnnotation = convertDeprecatedJavadocTag(owner)
|
val deprecatedAnnotation = convertDeprecatedJavadocTag(owner, target)
|
||||||
if (deprecatedAnnotation != null) {
|
if (deprecatedAnnotation != null) {
|
||||||
convertedAnnotations += deprecatedAnnotation
|
convertedAnnotations += deprecatedAnnotation
|
||||||
}
|
}
|
||||||
@@ -73,20 +73,29 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
return Annotations(convertedAnnotations).assignNoPrototype()
|
return Annotations(convertedAnnotations).assignNoPrototype()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertDeprecatedJavadocTag(element: PsiDocCommentOwner): Annotation? {
|
private fun convertDeprecatedJavadocTag(element: PsiDocCommentOwner, target: AnnotationUseTarget?): Annotation? {
|
||||||
val deprecatedTag = element.docComment?.findTagByName("deprecated") ?: return null
|
val deprecatedTag = element.docComment?.findTagByName("deprecated") ?: return null
|
||||||
val deferredExpression = converter.deferredElement<Expression> {
|
val deferredExpression = converter.deferredElement<Expression> {
|
||||||
LiteralExpression("\"" + StringUtil.escapeStringCharacters(deprecatedTag.content()) + "\"").assignNoPrototype()
|
LiteralExpression("\"" + StringUtil.escapeStringCharacters(deprecatedTag.content()) + "\"").assignNoPrototype()
|
||||||
}
|
}
|
||||||
return Annotation(Identifier("Deprecated").assignPrototype(deprecatedTag.nameElement),
|
val identifier = Identifier("Deprecated").assignPrototype(deprecatedTag.nameElement)
|
||||||
listOf(null to deferredExpression), newLineAfter = true)
|
return Annotation(identifier,
|
||||||
|
listOf(null to deferredExpression),
|
||||||
|
true,
|
||||||
|
effectiveAnnotationUseTarget(identifier.name, target))
|
||||||
.assignPrototype(deprecatedTag)
|
.assignPrototype(deprecatedTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertModifiersToAnnotations(owner: PsiModifierListOwner): Annotations {
|
private fun convertModifiersToAnnotations(owner: PsiModifierListOwner, target: AnnotationUseTarget?): Annotations {
|
||||||
val list = MODIFIER_TO_ANNOTATION
|
val list = MODIFIER_TO_ANNOTATION
|
||||||
.filter { owner.hasModifierProperty(it.first) }
|
.filter { owner.hasModifierProperty(it.first) }
|
||||||
.map { Annotation(Identifier.withNoPrototype(it.second), listOf(), newLineAfter = false).assignNoPrototype() }
|
.map {
|
||||||
|
Annotation(Identifier.withNoPrototype(it.second),
|
||||||
|
listOf(),
|
||||||
|
newLineAfter = false,
|
||||||
|
target = target
|
||||||
|
).assignNoPrototype()
|
||||||
|
}
|
||||||
return Annotations(list).assignNoPrototype()
|
return Annotations(list).assignNoPrototype()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +110,15 @@ class AnnotationConverter(private val converter: Converter) {
|
|||||||
return expr.referenceName?.let { JavaAnnotationTargetMapper.mapJavaTargetArgumentByName(it) } ?: emptySet()
|
return expr.referenceName?.let { JavaAnnotationTargetMapper.mapJavaTargetArgumentByName(it) } ?: emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertAnnotation(annotation: PsiAnnotation, newLineAfter: Boolean): Annotation? {
|
private fun effectiveAnnotationUseTarget(name: String, target: AnnotationUseTarget?): AnnotationUseTarget? =
|
||||||
|
when (name) {
|
||||||
|
"Deprecated" -> if (target == AnnotationUseTarget.Param) null else target
|
||||||
|
else -> target
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertAnnotation(annotation: PsiAnnotation, newLineAfter: Boolean, target: AnnotationUseTarget? = null): Annotation? {
|
||||||
val (name, arguments) = convertAnnotationValue(annotation) ?: return null
|
val (name, arguments) = convertAnnotationValue(annotation) ?: return null
|
||||||
return Annotation(name, arguments, newLineAfter).assignPrototype(annotation)
|
return Annotation(name, arguments, newLineAfter, effectiveAnnotationUseTarget(name.name, target)).assignPrototype(annotation, CommentsAndSpacesInheritance.NO_SPACES)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertAnnotationValue(annotation: PsiAnnotation): Pair<Identifier, List<Pair<Identifier?, DeferredElement<Expression>>>>? {
|
private fun convertAnnotationValue(annotation: PsiAnnotation): Pair<Identifier, List<Pair<Identifier?, DeferredElement<Expression>>>>? {
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ class ConstructorConverter(
|
|||||||
FunctionParameter(propertyInfo.identifier,
|
FunctionParameter(propertyInfo.identifier,
|
||||||
type,
|
type,
|
||||||
if (propertyInfo.isVar) FunctionParameter.VarValModifier.Var else FunctionParameter.VarValModifier.Val,
|
if (propertyInfo.isVar) FunctionParameter.VarValModifier.Var else FunctionParameter.VarValModifier.Val,
|
||||||
converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
|
converter.convertAnnotations(parameter, AnnotationUseTarget.Param) + converter.convertAnnotations(field),
|
||||||
propertyInfo.modifiers,
|
propertyInfo.modifiers,
|
||||||
default)
|
default)
|
||||||
.assignPrototypes(
|
.assignPrototypes(
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ class Converter private constructor(
|
|||||||
return File(convertedChildren).assignPrototype(javaFile)
|
return File(convertedChildren).assignPrototype(javaFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertAnnotations(owner: PsiModifierListOwner): Annotations
|
fun convertAnnotations(owner: PsiModifierListOwner, target: AnnotationUseTarget? = null): Annotations
|
||||||
= annotationConverter.convertAnnotations(owner)
|
= annotationConverter.convertAnnotations(owner, target)
|
||||||
|
|
||||||
fun convertClass(psiClass: PsiClass): Class {
|
fun convertClass(psiClass: PsiClass): Class {
|
||||||
if (psiClass.isAnnotationType) {
|
if (psiClass.isAnnotationType) {
|
||||||
@@ -320,10 +320,19 @@ class Converter private constructor(
|
|||||||
val getMethod = propertyInfo.getMethod
|
val getMethod = propertyInfo.getMethod
|
||||||
val setMethod = propertyInfo.setMethod
|
val setMethod = propertyInfo.setMethod
|
||||||
|
|
||||||
//TODO: annotations from getter/setter?
|
|
||||||
val annotations = field?.let { convertAnnotations(it) } ?: Annotations.Empty
|
|
||||||
|
|
||||||
val modifiers = (propertyInfo.modifiers + (field?.let{ specialModifiersCase(field) } ?: Modifiers.Empty))
|
var annotations = Annotations.Empty
|
||||||
|
|
||||||
|
field?.let { annotations += convertAnnotations(it) }
|
||||||
|
|
||||||
|
if (!propertyInfo.needExplicitGetter)
|
||||||
|
getMethod?.let { annotations += convertAnnotations(it, AnnotationUseTarget.Get) }
|
||||||
|
|
||||||
|
if (!propertyInfo.needExplicitSetter)
|
||||||
|
setMethod?.let { annotations += convertAnnotations(it, AnnotationUseTarget.Set) }
|
||||||
|
|
||||||
|
|
||||||
|
val modifiers = (propertyInfo.modifiers + (field?.let { specialModifiersCase(field) } ?: Modifiers.Empty))
|
||||||
|
|
||||||
val name = propertyInfo.identifier
|
val name = propertyInfo.identifier
|
||||||
if (field is PsiEnumConstant) {
|
if (field is PsiEnumConstant) {
|
||||||
|
|||||||
@@ -20,9 +20,20 @@ import org.jetbrains.kotlin.j2k.CodeBuilder
|
|||||||
import org.jetbrains.kotlin.j2k.append
|
import org.jetbrains.kotlin.j2k.append
|
||||||
import org.jetbrains.kotlin.j2k.buildList
|
import org.jetbrains.kotlin.j2k.buildList
|
||||||
|
|
||||||
class Annotation(val name: Identifier, val arguments: List<Pair<Identifier?, DeferredElement<Expression>>>, val newLineAfter: Boolean) : Element() {
|
enum class AnnotationUseTarget(val id: String) {
|
||||||
|
File("file"), Param("param"), Get("get"), Set("set")
|
||||||
|
}
|
||||||
|
|
||||||
|
class Annotation(val name: Identifier,
|
||||||
|
val arguments: List<Pair<Identifier?, DeferredElement<Expression>>>,
|
||||||
|
val newLineAfter: Boolean,
|
||||||
|
val target: AnnotationUseTarget? = null) : Element() {
|
||||||
|
|
||||||
override fun generateCode(builder: CodeBuilder) {
|
override fun generateCode(builder: CodeBuilder) {
|
||||||
builder.append("@")
|
builder.append("@")
|
||||||
|
target?.let {
|
||||||
|
builder.append("${it.id}:")
|
||||||
|
}
|
||||||
if (arguments.isEmpty()) {
|
if (arguments.isEmpty()) {
|
||||||
builder.append(name)
|
builder.append(name)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
@interface A {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class U {
|
||||||
|
@B
|
||||||
|
public int i;
|
||||||
|
|
||||||
|
public U(@A int i) {
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
internal annotation class A
|
||||||
|
|
||||||
|
internal annotation class B
|
||||||
|
|
||||||
|
class U(@param:A @B
|
||||||
|
var i: Int)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
@interface An {
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@An(value = "get")
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@An(value = "set")
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
internal annotation class An(val value: String)
|
||||||
|
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
@get:An(value = "get")
|
||||||
|
@set:An(value = "set")
|
||||||
|
var id: Int = 0
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
public class WithModifiersOnAccessors {
|
||||||
|
private synchronized void methSync() {}
|
||||||
|
protected strictfp void methStrict() {}
|
||||||
|
|
||||||
|
private int sync = 0;
|
||||||
|
public synchronized int getSync() { return sync; }
|
||||||
|
public synchronized void setSync(int sync) { this.sync = sync; }
|
||||||
|
|
||||||
|
public double strict = 0.0;
|
||||||
|
public strictfp double getStrict() { return strict; }
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
class WithModifiersOnAccessors {
|
||||||
|
@Synchronized private fun methSync() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Strictfp protected fun methStrict() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized @set:Synchronized var sync = 0
|
||||||
|
|
||||||
|
@get:Strictfp var strict = 0.0
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import javaApi.Anon5
|
import javaApi.Anon5
|
||||||
|
|
||||||
internal class A
|
internal class A @Anon5(10)
|
||||||
@Anon5(10)
|
|
||||||
constructor(private val a: Int, private val b: Int) {
|
constructor(private val a: Int, private val b: Int) {
|
||||||
|
|
||||||
@Deprecated("") // this constructor will not be replaced by default parameter value in primary because of this annotation
|
@Deprecated("") // this constructor will not be replaced by default parameter value in primary because of this annotation
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationFromConstructorParamToProperty.java")
|
||||||
|
public void testAnnotationFromConstructorParamToProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationFromConstructorParamToProperty.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("annotationInterface1.java")
|
@TestMetadata("annotationInterface1.java")
|
||||||
public void testAnnotationInterface1() throws Exception {
|
public void testAnnotationInterface1() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
||||||
@@ -86,6 +92,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationsFromPropertyAccessors.java")
|
||||||
|
public void testAnnotationsFromPropertyAccessors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationsFromPropertyAccessors.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("javaClassArgument.java")
|
@TestMetadata("javaClassArgument.java")
|
||||||
public void testJavaClassArgument() throws Exception {
|
public void testJavaClassArgument() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java");
|
||||||
@@ -116,6 +128,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("modifiersToAnnotationsFromPropertyAccessors.java")
|
||||||
|
public void testModifiersToAnnotationsFromPropertyAccessors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/modifiersToAnnotationsFromPropertyAccessors.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("primaryConstructorAnnotation.java")
|
@TestMetadata("primaryConstructorAnnotation.java")
|
||||||
public void testPrimaryConstructorAnnotation() throws Exception {
|
public void testPrimaryConstructorAnnotation() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/primaryConstructorAnnotation.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/primaryConstructorAnnotation.java");
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationFromConstructorParamToProperty.java")
|
||||||
|
public void testAnnotationFromConstructorParamToProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationFromConstructorParamToProperty.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("annotationInterface1.java")
|
@TestMetadata("annotationInterface1.java")
|
||||||
public void testAnnotationInterface1() throws Exception {
|
public void testAnnotationInterface1() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
|
||||||
@@ -86,6 +92,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationsFromPropertyAccessors.java")
|
||||||
|
public void testAnnotationsFromPropertyAccessors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationsFromPropertyAccessors.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("javaClassArgument.java")
|
@TestMetadata("javaClassArgument.java")
|
||||||
public void testJavaClassArgument() throws Exception {
|
public void testJavaClassArgument() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java");
|
||||||
@@ -116,6 +128,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("modifiersToAnnotationsFromPropertyAccessors.java")
|
||||||
|
public void testModifiersToAnnotationsFromPropertyAccessors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/modifiersToAnnotationsFromPropertyAccessors.java");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("primaryConstructorAnnotation.java")
|
@TestMetadata("primaryConstructorAnnotation.java")
|
||||||
public void testPrimaryConstructorAnnotation() throws Exception {
|
public void testPrimaryConstructorAnnotation() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/primaryConstructorAnnotation.java");
|
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/primaryConstructorAnnotation.java");
|
||||||
|
|||||||
Reference in New Issue
Block a user