New J2K: Handle correctly modifiers of class members when there is no parent class in the AST

This commit is contained in:
Ilya Kirillov
2019-04-01 14:08:34 +03:00
committed by Ilya Kirillov
parent c05a01e12b
commit 854fc6a7d7
7 changed files with 126 additions and 95 deletions
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.nj2k.conversions.*
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
import org.jetbrains.kotlin.nj2k.tree.JKTreeRoot
import org.jetbrains.kotlin.nj2k.tree.impl.JKTreeRootImpl
object ConversionsRunner {
@@ -73,7 +75,7 @@ object ConversionsRunner {
+EqualsOperatorConversion(context)
+TypeMappingConversion(context)
+ImplicitCastsConversion(context)
+InternalDeclarationConversion()
+InternalDeclarationConversion(context)
//Kotlin --> Kotlin conversions
+InnerClassConversion()
@@ -103,7 +103,7 @@ class JavaToJKTreeBuilder constructor(
}
private fun PsiImportStatement.toJK(): JKImportStatementImpl {
private fun PsiImportStatement.toJK(): JKImportStatement {
val target = resolve()
val rawName = text.substringAfter("import").substringBeforeLast(";").trim()
val name =
@@ -517,21 +517,11 @@ class JavaToJKTreeBuilder constructor(
it.assignNonCodeElements(this)
}
fun PsiClass.toJK(): JKClass {
val classKind: JKClass.ClassKind = when {
isAnnotationType -> JKClass.ClassKind.ANNOTATION
isEnum -> JKClass.ClassKind.ENUM
isInterface -> JKClass.ClassKind.INTERFACE
else -> JKClass.ClassKind.CLASS
}
fun PsiReferenceList.mapTypes() =
this.referencedTypes.map { JKTypeElementImpl(it.toJK(symbolProvider, Nullability.Default)) }
return JKClassImpl(
fun PsiClass.toJK(): JKClass =
JKClassImpl(
nameIdentifier.toJK(),
inheritanceInfo(),
classKind,
classKind(),
typeParameterList?.toJK() ?: JKTypeParameterListImpl(),
createClassBody(),
annotationList(this),
@@ -544,7 +534,7 @@ class JavaToJKTreeBuilder constructor(
}.also {
it.assignNonCodeElements(this)
}
}
fun PsiClass.inheritanceInfo(): JKInheritanceInfo {
val implTypes =
@@ -601,18 +591,7 @@ class JavaToJKTreeBuilder constructor(
fun PsiMember.modality() =
modifierList?.children?.mapNotNull { child ->
if (child !is PsiKeyword) return@mapNotNull null
when (child.text) {
PsiModifier.FINAL -> Modality.FINAL
PsiModifier.ABSTRACT -> Modality.ABSTRACT
else -> null
}?.let {
JKModalityModifierElementImpl(it).withAssignedNonCodeElements(child)
}
}?.firstOrNull() ?: JKModalityModifierElementImpl(Modality.OPEN)
modality({ ast, psi -> ast.assignNonCodeElements(psi) })
fun PsiMember.extraModifiers() =
modifierList?.children?.mapNotNull { child ->
@@ -631,56 +610,9 @@ class JavaToJKTreeBuilder constructor(
}
}.orEmpty()
fun PsiMember.visibility() =
modifierList?.children?.mapNotNull { child ->
if (child !is PsiKeyword) return@mapNotNull null
when (child.text) {
PsiModifier.PACKAGE_LOCAL -> Visibility.PACKAGE_PRIVATE
PsiModifier.PRIVATE -> Visibility.PRIVATE
PsiModifier.PROTECTED -> handleProtectedVisibility()
PsiModifier.PUBLIC -> Visibility.PUBLIC
else -> null
}?.let {
JKVisibilityModifierElementImpl(it).withAssignedNonCodeElements(child)
}
}?.firstOrNull() ?: JKVisibilityModifierElementImpl(Visibility.PACKAGE_PRIVATE)
private fun PsiMember.handleProtectedVisibility(): Visibility {
val originalClass = containingClass ?: return Visibility.PROTECTED
// Search for usages only in Java because java-protected member cannot be used in Kotlin from same package
val usages = referenceSearcher.findUsagesForExternalCodeProcessing(this, true, false)
return if (usages.any { !allowProtected(it.element, this, originalClass) })
Visibility.PUBLIC
else Visibility.PROTECTED
}
private fun allowProtected(element: PsiElement, member: PsiMember, originalClass: PsiClass): Boolean {
if (element.parent is PsiNewExpression && member is PsiMethod && member.isConstructor) {
// calls to for protected constructors are allowed only within same class or as super calls
return element.parentsWithSelf.contains(originalClass)
}
return element.parentsWithSelf.filterIsInstance<PsiClass>().any { accessContainingClass ->
if (!InheritanceUtil.isInheritorOrSelf(accessContainingClass, originalClass, true)) return@any false
if (element !is PsiReferenceExpression) return@any true
val qualifierExpression = element.qualifierExpression ?: return@any true
// super.foo is allowed if 'foo' is protected
if (qualifierExpression is PsiSuperExpression) return@any true
val receiverType = qualifierExpression.type ?: return@any true
val resolvedClass = PsiUtil.resolveGenericsClassInType(receiverType).element ?: return@any true
// receiver type should be subtype of containing class
InheritanceUtil.isInheritorOrSelf(resolvedClass, accessContainingClass, true)
}
}
private fun PsiMember.visibility(): JKVisibilityModifierElement =
visibility(referenceSearcher) { ast, psi -> ast.assignNonCodeElements(psi) }
fun PsiField.toJK(): JKJavaField {
return JKJavaFieldImpl(
@@ -5,14 +5,35 @@
package org.jetbrains.kotlin.nj2k.conversions
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.nj2k.ConversionContext
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.impl.psi
import org.jetbrains.kotlin.nj2k.visibility
class InternalDeclarationConversion : RecursiveApplicableConversionBase() {
class InternalDeclarationConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element !is JKVisibilityOwner || element !is JKModalityOwner) return recurse(element)
val containingClass = element.parentOfType<JKClass>() ?: return recurse(element)
val containingClass = element.parentOfType<JKClass>()
if (containingClass.visibility == Visibility.INTERNAL
if (element is JKClass && element.isLocalClass()) {
element.visibility = Visibility.PUBLIC
}
val containingClassVisibility =
containingClass?.visibility
?: element.psi<PsiMember>()
?.containingClass
?.visibility(context.converter.oldConverterServices.referenceSearcher, null)
?.visibility
val containingClassKind =
containingClass?.classKind
?: element.psi<PsiMember>()
?.containingClass
?.classKind
if (containingClassVisibility == Visibility.INTERNAL
&& element.visibility == Visibility.INTERNAL
&& element.modality == Modality.FINAL
&& (element is JKMethod || element is JKField)
@@ -20,11 +41,11 @@ class InternalDeclarationConversion : RecursiveApplicableConversionBase() {
element.visibility = Visibility.PUBLIC
}
if (containingClass.classKind == JKClass.ClassKind.INTERFACE) {
if (containingClassKind == JKClass.ClassKind.INTERFACE) {
element.visibility = Visibility.PUBLIC
}
if (containingClass.classKind == JKClass.ClassKind.ENUM
if (containingClassKind == JKClass.ClassKind.ENUM
&& element is JKKtPrimaryConstructor
) {
element.visibility = Visibility.PRIVATE
@@ -12,15 +12,6 @@ import org.jetbrains.kotlin.nj2k.tree.*
class JavaModifiersConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element is JKVisibilityOwner) {
if (element.visibility == Visibility.PACKAGE_PRIVATE) {
if (element is JKClass && element.isLocalClass()) {
element.visibility = Visibility.PUBLIC
} else {
element.visibility = Visibility.INTERNAL
}
}
}
if (element is JKModalityOwner && element is JKAnnotationListOwner) {
val overrideAnnotation = element.annotationList.annotationByFqName("java.lang.Override")
if (overrideAnnotation != null) {
@@ -10,10 +10,10 @@ import org.jetbrains.kotlin.nj2k.tree.impl.JKBranchElementBase
abstract class MatchBasedConversion : SequentialBaseConversion {
fun <R : JKTreeElement, T> applyRecursive(element: R, data: T, func: (JKTreeElement, T) -> JKTreeElement): R =
fun <R : JKTreeElement, T> applyRecursive(element: R, data: T, func: (JKTreeElement, T) -> JKTreeElement): R =
org.jetbrains.kotlin.nj2k.tree.applyRecursive(element, data, ::onElementChanged, func)
inline fun <R : JKTreeElement> applyRecursive(element: R, crossinline func: (JKTreeElement) -> JKTreeElement): R {
fun <R : JKTreeElement> applyRecursive(element: R, func: (JKTreeElement) -> JKTreeElement): R {
return applyRecursive(element, null) { it, _ -> func(it) }
}
+87 -1
View File
@@ -8,8 +8,15 @@ package org.jetbrains.kotlin.nj2k
import com.intellij.codeInsight.generation.GenerateEqualsHelper.getEqualsSignature
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.j2k.ReferenceSearcher
import org.jetbrains.kotlin.j2k.isNullLiteral
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.impl.JKModalityModifierElementImpl
import org.jetbrains.kotlin.nj2k.tree.impl.JKVisibilityModifierElementImpl
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
//copied from old j2k
fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
@@ -34,5 +41,84 @@ fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
else -> return false
}
}
}
internal fun PsiMember.visibility(
referenceSearcher: ReferenceSearcher,
assignNonCodeElements: ((JKNonCodeElementsListOwner, PsiElement) -> Unit)?
): JKVisibilityModifierElement =
modifierList?.children?.mapNotNull { child ->
if (child !is PsiKeyword) return@mapNotNull null
when (child.text) {
PsiModifier.PACKAGE_LOCAL -> Visibility.INTERNAL
PsiModifier.PRIVATE -> Visibility.PRIVATE
PsiModifier.PROTECTED -> handleProtectedVisibility(referenceSearcher)
PsiModifier.PUBLIC -> Visibility.PUBLIC
else -> null
}?.let {
JKVisibilityModifierElementImpl(it)
}?.also { modifier ->
assignNonCodeElements?.also { it(modifier, child) }
}
}?.firstOrNull() ?: JKVisibilityModifierElementImpl(Visibility.INTERNAL)
fun PsiMember.modality(assignNonCodeElements: ((JKNonCodeElementsListOwner, PsiElement) -> Unit)?) =
modifierList?.children?.mapNotNull { child ->
if (child !is PsiKeyword) return@mapNotNull null
when (child.text) {
PsiModifier.FINAL -> Modality.FINAL
PsiModifier.ABSTRACT -> Modality.ABSTRACT
else -> null
}?.let {
JKModalityModifierElementImpl(it)
}?.also { modifier ->
assignNonCodeElements?.let { it(modifier, child) }
}
}?.firstOrNull() ?: JKModalityModifierElementImpl(Modality.OPEN)
private fun PsiMember.handleProtectedVisibility(referenceSearcher: ReferenceSearcher): Visibility {
val originalClass = containingClass ?: return Visibility.PROTECTED
// Search for usages only in Java because java-protected member cannot be used in Kotlin from same package
val usages = referenceSearcher.findUsagesForExternalCodeProcessing(this, true, false)
return if (usages.any { !allowProtected(it.element, this, originalClass) })
Visibility.PUBLIC
else Visibility.PROTECTED
}
private fun allowProtected(element: PsiElement, member: PsiMember, originalClass: PsiClass): Boolean {
if (element.parent is PsiNewExpression && member is PsiMethod && member.isConstructor) {
// calls to for protected constructors are allowed only within same class or as super calls
return element.parentsWithSelf.contains(originalClass)
}
return element.parentsWithSelf.filterIsInstance<PsiClass>().any { accessContainingClass ->
if (!InheritanceUtil.isInheritorOrSelf(accessContainingClass, originalClass, true)) return@any false
if (element !is PsiReferenceExpression) return@any true
val qualifierExpression = element.qualifierExpression ?: return@any true
// super.foo is allowed if 'foo' is protected
if (qualifierExpression is PsiSuperExpression) return@any true
val receiverType = qualifierExpression.type ?: return@any true
val resolvedClass = PsiUtil.resolveGenericsClassInType(receiverType).element ?: return@any true
// receiver type should be subtype of containing class
InheritanceUtil.isInheritorOrSelf(resolvedClass, accessContainingClass, true)
}
}
fun PsiClass.classKind(): JKClass.ClassKind =
when {
isAnnotationType -> JKClass.ClassKind.ANNOTATION
isEnum -> JKClass.ClassKind.ENUM
isInterface -> JKClass.ClassKind.INTERFACE
else -> JKClass.ClassKind.CLASS
}
@@ -211,7 +211,6 @@ interface JKVisibilityOwner : JKModifiersListOwner {
enum class Visibility(override val text: String) : Modifier {
PUBLIC("public"),
INTERNAL("internal"),
PACKAGE_PRIVATE(""),
PROTECTED("protected"),
PRIVATE("private")
}