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.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) }
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user