Fix for KT-15027 J2K: Annotations are set on functions, but not on property accessors

#KT-15027 fixed
This commit is contained in:
Simon Ogorodnik
2016-12-13 19:34:57 +03:00
committed by Simon Ogorodnik
parent 5071baf970
commit 73ea0e8460
13 changed files with 162 additions and 22 deletions
@@ -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(
+14 -5
View File
@@ -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)
} }
@@ -0,0 +1,16 @@
@interface A {
}
@interface B {
}
public class U {
@B
public int i;
public U(@A int i) {
this.i = i;
}
}
@@ -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
}
@@ -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; }
}
@@ -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");