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
}
fun convertAnnotations(owner: PsiModifierListOwner): Annotations
= convertAnnotationsOnly(owner) + convertModifiersToAnnotations(owner)
fun convertAnnotations(owner: PsiModifierListOwner, target: AnnotationUseTarget? = null): Annotations
= 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 annotations = modifierList?.annotations?.filter { it.qualifiedName !in annotationsToRemove }
var convertedAnnotations: List<Annotation> = if (annotations != null && annotations.isNotEmpty()) {
val newLines = if (!modifierList!!.isInSingleLine()) {
val newLines = if (!modifierList.isInSingleLine()) {
true
}
else {
@@ -57,14 +57,14 @@ class AnnotationConverter(private val converter: Converter) {
if (child is PsiWhiteSpace) !child.isInSingleLine() else false
}
annotations.mapNotNull { convertAnnotation(it, newLineAfter = newLines) }
annotations.mapNotNull { convertAnnotation(it, newLineAfter = newLines, target = target) }
}
else {
listOf()
}
if (owner is PsiDocCommentOwner) {
val deprecatedAnnotation = convertDeprecatedJavadocTag(owner)
val deprecatedAnnotation = convertDeprecatedJavadocTag(owner, target)
if (deprecatedAnnotation != null) {
convertedAnnotations += deprecatedAnnotation
}
@@ -73,20 +73,29 @@ class AnnotationConverter(private val converter: Converter) {
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 deferredExpression = converter.deferredElement<Expression> {
LiteralExpression("\"" + StringUtil.escapeStringCharacters(deprecatedTag.content()) + "\"").assignNoPrototype()
}
return Annotation(Identifier("Deprecated").assignPrototype(deprecatedTag.nameElement),
listOf(null to deferredExpression), newLineAfter = true)
val identifier = Identifier("Deprecated").assignPrototype(deprecatedTag.nameElement)
return Annotation(identifier,
listOf(null to deferredExpression),
true,
effectiveAnnotationUseTarget(identifier.name, target))
.assignPrototype(deprecatedTag)
}
private fun convertModifiersToAnnotations(owner: PsiModifierListOwner): Annotations {
private fun convertModifiersToAnnotations(owner: PsiModifierListOwner, target: AnnotationUseTarget?): Annotations {
val list = MODIFIER_TO_ANNOTATION
.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()
}
@@ -101,9 +110,15 @@ class AnnotationConverter(private val converter: Converter) {
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
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>>>>? {
@@ -203,7 +203,7 @@ class ConstructorConverter(
FunctionParameter(propertyInfo.identifier,
type,
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,
default)
.assignPrototypes(
+14 -5
View File
@@ -160,8 +160,8 @@ class Converter private constructor(
return File(convertedChildren).assignPrototype(javaFile)
}
fun convertAnnotations(owner: PsiModifierListOwner): Annotations
= annotationConverter.convertAnnotations(owner)
fun convertAnnotations(owner: PsiModifierListOwner, target: AnnotationUseTarget? = null): Annotations
= annotationConverter.convertAnnotations(owner, target)
fun convertClass(psiClass: PsiClass): Class {
if (psiClass.isAnnotationType) {
@@ -320,10 +320,19 @@ class Converter private constructor(
val getMethod = propertyInfo.getMethod
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
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.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) {
builder.append("@")
target?.let {
builder.append("${it.id}:")
}
if (arguments.isEmpty()) {
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
internal class A
@Anon5(10)
internal class A @Anon5(10)
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
@@ -56,6 +56,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
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")
public void testAnnotationInterface1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
@@ -86,6 +92,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
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")
public void testJavaClassArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java");
@@ -116,6 +128,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
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")
public void testPrimaryConstructorAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/primaryConstructorAnnotation.java");
@@ -56,6 +56,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testAnnotationInterface1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/annotationInterface1.java");
@@ -86,6 +92,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testJavaClassArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java");
@@ -116,6 +128,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testPrimaryConstructorAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/primaryConstructorAnnotation.java");