New J2K: Handle correctly modifiers of class members when there is no parent class in the AST
This commit is contained in:
committed by
Ilya Kirillov
parent
c05a01e12b
commit
854fc6a7d7
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.nj2k
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.nj2k.conversions.*
|
import org.jetbrains.kotlin.nj2k.conversions.*
|
||||||
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
|
import org.jetbrains.kotlin.nj2k.tree.JKTreeElement
|
||||||
|
import org.jetbrains.kotlin.nj2k.tree.JKTreeRoot
|
||||||
|
import org.jetbrains.kotlin.nj2k.tree.impl.JKTreeRootImpl
|
||||||
|
|
||||||
object ConversionsRunner {
|
object ConversionsRunner {
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ object ConversionsRunner {
|
|||||||
+EqualsOperatorConversion(context)
|
+EqualsOperatorConversion(context)
|
||||||
+TypeMappingConversion(context)
|
+TypeMappingConversion(context)
|
||||||
+ImplicitCastsConversion(context)
|
+ImplicitCastsConversion(context)
|
||||||
+InternalDeclarationConversion()
|
+InternalDeclarationConversion(context)
|
||||||
|
|
||||||
//Kotlin --> Kotlin conversions
|
//Kotlin --> Kotlin conversions
|
||||||
+InnerClassConversion()
|
+InnerClassConversion()
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class JavaToJKTreeBuilder constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun PsiImportStatement.toJK(): JKImportStatementImpl {
|
private fun PsiImportStatement.toJK(): JKImportStatement {
|
||||||
val target = resolve()
|
val target = resolve()
|
||||||
val rawName = text.substringAfter("import").substringBeforeLast(";").trim()
|
val rawName = text.substringAfter("import").substringBeforeLast(";").trim()
|
||||||
val name =
|
val name =
|
||||||
@@ -517,21 +517,11 @@ class JavaToJKTreeBuilder constructor(
|
|||||||
it.assignNonCodeElements(this)
|
it.assignNonCodeElements(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PsiClass.toJK(): JKClass {
|
fun PsiClass.toJK(): JKClass =
|
||||||
val classKind: JKClass.ClassKind = when {
|
JKClassImpl(
|
||||||
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(
|
|
||||||
nameIdentifier.toJK(),
|
nameIdentifier.toJK(),
|
||||||
inheritanceInfo(),
|
inheritanceInfo(),
|
||||||
classKind,
|
classKind(),
|
||||||
typeParameterList?.toJK() ?: JKTypeParameterListImpl(),
|
typeParameterList?.toJK() ?: JKTypeParameterListImpl(),
|
||||||
createClassBody(),
|
createClassBody(),
|
||||||
annotationList(this),
|
annotationList(this),
|
||||||
@@ -544,7 +534,7 @@ class JavaToJKTreeBuilder constructor(
|
|||||||
}.also {
|
}.also {
|
||||||
it.assignNonCodeElements(this)
|
it.assignNonCodeElements(this)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun PsiClass.inheritanceInfo(): JKInheritanceInfo {
|
fun PsiClass.inheritanceInfo(): JKInheritanceInfo {
|
||||||
val implTypes =
|
val implTypes =
|
||||||
@@ -601,18 +591,7 @@ class JavaToJKTreeBuilder constructor(
|
|||||||
|
|
||||||
|
|
||||||
fun PsiMember.modality() =
|
fun PsiMember.modality() =
|
||||||
modifierList?.children?.mapNotNull { child ->
|
modality({ ast, psi -> ast.assignNonCodeElements(psi) })
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
fun PsiMember.extraModifiers() =
|
fun PsiMember.extraModifiers() =
|
||||||
modifierList?.children?.mapNotNull { child ->
|
modifierList?.children?.mapNotNull { child ->
|
||||||
@@ -631,56 +610,9 @@ class JavaToJKTreeBuilder constructor(
|
|||||||
}
|
}
|
||||||
}.orEmpty()
|
}.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 {
|
fun PsiField.toJK(): JKJavaField {
|
||||||
return JKJavaFieldImpl(
|
return JKJavaFieldImpl(
|
||||||
|
|||||||
@@ -5,14 +5,35 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.nj2k.conversions
|
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.*
|
||||||
|
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 {
|
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
||||||
if (element !is JKVisibilityOwner || element !is JKModalityOwner) return recurse(element)
|
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.visibility == Visibility.INTERNAL
|
||||||
&& element.modality == Modality.FINAL
|
&& element.modality == Modality.FINAL
|
||||||
&& (element is JKMethod || element is JKField)
|
&& (element is JKMethod || element is JKField)
|
||||||
@@ -20,11 +41,11 @@ class InternalDeclarationConversion : RecursiveApplicableConversionBase() {
|
|||||||
element.visibility = Visibility.PUBLIC
|
element.visibility = Visibility.PUBLIC
|
||||||
}
|
}
|
||||||
|
|
||||||
if (containingClass.classKind == JKClass.ClassKind.INTERFACE) {
|
if (containingClassKind == JKClass.ClassKind.INTERFACE) {
|
||||||
element.visibility = Visibility.PUBLIC
|
element.visibility = Visibility.PUBLIC
|
||||||
}
|
}
|
||||||
|
|
||||||
if (containingClass.classKind == JKClass.ClassKind.ENUM
|
if (containingClassKind == JKClass.ClassKind.ENUM
|
||||||
&& element is JKKtPrimaryConstructor
|
&& element is JKKtPrimaryConstructor
|
||||||
) {
|
) {
|
||||||
element.visibility = Visibility.PRIVATE
|
element.visibility = Visibility.PRIVATE
|
||||||
|
|||||||
@@ -12,15 +12,6 @@ import org.jetbrains.kotlin.nj2k.tree.*
|
|||||||
|
|
||||||
class JavaModifiersConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
|
class JavaModifiersConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
|
||||||
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
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) {
|
if (element is JKModalityOwner && element is JKAnnotationListOwner) {
|
||||||
val overrideAnnotation = element.annotationList.annotationByFqName("java.lang.Override")
|
val overrideAnnotation = element.annotationList.annotationByFqName("java.lang.Override")
|
||||||
if (overrideAnnotation != null) {
|
if (overrideAnnotation != null) {
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import org.jetbrains.kotlin.nj2k.tree.impl.JKBranchElementBase
|
|||||||
|
|
||||||
abstract class MatchBasedConversion : SequentialBaseConversion {
|
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)
|
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) }
|
return applyRecursive(element, null) { it, _ -> func(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,15 @@ package org.jetbrains.kotlin.nj2k
|
|||||||
import com.intellij.codeInsight.generation.GenerateEqualsHelper.getEqualsSignature
|
import com.intellij.codeInsight.generation.GenerateEqualsHelper.getEqualsSignature
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.util.InheritanceUtil
|
||||||
import com.intellij.psi.util.MethodSignatureUtil
|
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.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
|
//copied from old j2k
|
||||||
fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
|
fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
|
||||||
@@ -34,5 +41,84 @@ fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean {
|
|||||||
|
|
||||||
else -> return false
|
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 {
|
enum class Visibility(override val text: String) : Modifier {
|
||||||
PUBLIC("public"),
|
PUBLIC("public"),
|
||||||
INTERNAL("internal"),
|
INTERNAL("internal"),
|
||||||
PACKAGE_PRIVATE(""),
|
|
||||||
PROTECTED("protected"),
|
PROTECTED("protected"),
|
||||||
PRIVATE("private")
|
PRIVATE("private")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user