Fix CCE in constructNestedClassReferenceIdentifier J2K

#KT-17712 fixed
This commit is contained in:
Simon Ogorodnik
2017-05-19 20:02:55 +03:00
parent d0991fffd2
commit 6a8bb8b33d
7 changed files with 19 additions and 36 deletions
@@ -123,15 +123,14 @@ class ConstructorConverter(
val body = primaryConstructor.body val body = primaryConstructor.body
val parameterUsageReplacementMap = HashMap<String, String>() val parameterUsageReplacementMap = HashMap<String, String>()
val correctedTypeConverter = converter.withSpecialContext(psiClass).typeConverter /* to correct nested class references */
val bodyGenerator: (CodeConverter) -> Block = if (body != null) { val bodyGenerator: (CodeConverter) -> Block = if (body != null) {
val statementsToRemove = HashSet<PsiStatement>() val statementsToRemove = HashSet<PsiStatement>()
for (parameter in params) { for (parameter in params) {
val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, primaryConstructor) ?: continue val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, primaryConstructor) ?: continue
val fieldType = correctedTypeConverter.convertVariableType(field) val fieldType = converter.typeConverter.convertVariableType(field)
val parameterType = correctedTypeConverter.convertVariableType(parameter) val parameterType = converter.typeConverter.convertVariableType(parameter)
// types can be different only in nullability // types can be different only in nullability
val type = if (fieldType == parameterType) { val type = if (fieldType == parameterType) {
fieldType fieldType
@@ -175,27 +174,25 @@ class ConstructorConverter(
// we need to replace renamed parameter usages in base class constructor arguments and in default values // we need to replace renamed parameter usages in base class constructor arguments and in default values
val correctedConverter = converter.withSpecialContext(psiClass) /* to correct nested class references */
fun CodeConverter.correct() = withSpecialExpressionConverter(ReplacingExpressionConverter(parameterUsageReplacementMap)) fun CodeConverter.correct() = withSpecialExpressionConverter(ReplacingExpressionConverter(parameterUsageReplacementMap))
val statement = primaryConstructor.body?.statements?.firstOrNull() val statement = primaryConstructor.body?.statements?.firstOrNull()
val methodCall = (statement as? PsiExpressionStatement)?.expression as? PsiMethodCallExpression val methodCall = (statement as? PsiExpressionStatement)?.expression as? PsiMethodCallExpression
if (methodCall != null && methodCall.isSuperConstructorCall()) { if (methodCall != null && methodCall.isSuperConstructorCall()) {
baseClassParams = methodCall.argumentList.expressions.map { baseClassParams = methodCall.argumentList.expressions.map {
correctedConverter.deferredElement { codeConverter -> codeConverter.correct().convertExpression(it) } converter.deferredElement { codeConverter -> codeConverter.correct().convertExpression(it) }
} }
} }
else { else {
baseClassParams = emptyList() baseClassParams = emptyList()
} }
val parameterList = correctedConverter.convertParameterList( val parameterList = converter.convertParameterList(
primaryConstructor, primaryConstructor,
overloadReducer, overloadReducer,
{ parameter, default -> { parameter, default ->
if (!parameterToField.containsKey(parameter)) { if (!parameterToField.containsKey(parameter)) {
correctedConverter.convertParameter(parameter, defaultValue = default) converter.convertParameter(parameter, defaultValue = default)
} }
else { else {
val (field, type) = parameterToField[parameter]!! val (field, type) = parameterToField[parameter]!!
+6 -17
View File
@@ -41,8 +41,7 @@ class Converter private constructor(
val settings: ConverterSettings, val settings: ConverterSettings,
val inConversionScope: (PsiElement) -> Boolean, val inConversionScope: (PsiElement) -> Boolean,
val services: JavaToKotlinConverterServices, val services: JavaToKotlinConverterServices,
private val commonState: Converter.CommonState, private val commonState: Converter.CommonState
private val personalState: Converter.PersonalState
) { ) {
// state which is shared between all converter's based on this one // state which is shared between all converter's based on this one
@@ -51,15 +50,10 @@ class Converter private constructor(
val postUnfoldActions = ArrayList<() -> Unit>() val postUnfoldActions = ArrayList<() -> Unit>()
} }
// state which may differ in different converter's
class PersonalState(val specialContext: PsiElement?)
val project: Project = elementToConvert.project val project: Project = elementToConvert.project
val typeConverter: TypeConverter = TypeConverter(this) val typeConverter: TypeConverter = TypeConverter(this)
val annotationConverter: AnnotationConverter = AnnotationConverter(this) val annotationConverter: AnnotationConverter = AnnotationConverter(this)
val specialContext: PsiElement? = personalState.specialContext
val referenceSearcher: ReferenceSearcher = CachingReferenceSearcher(services.referenceSearcher) val referenceSearcher: ReferenceSearcher = CachingReferenceSearcher(services.referenceSearcher)
val propertyDetectionCache = PropertyDetectionCache(this) val propertyDetectionCache = PropertyDetectionCache(this)
@@ -68,15 +62,10 @@ class Converter private constructor(
fun create(elementToConvert: PsiElement, settings: ConverterSettings, services: JavaToKotlinConverterServices, fun create(elementToConvert: PsiElement, settings: ConverterSettings, services: JavaToKotlinConverterServices,
inConversionScope: (PsiElement) -> Boolean, usageProcessingsCollector: (UsageProcessing) -> Unit): Converter { inConversionScope: (PsiElement) -> Boolean, usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
return Converter(elementToConvert, settings, inConversionScope, return Converter(elementToConvert, settings, inConversionScope,
services, CommonState(usageProcessingsCollector), PersonalState(null)) services, CommonState(usageProcessingsCollector))
} }
} }
fun withSpecialContext(context: PsiElement): Converter = withState(PersonalState(context))
private fun withState(state: PersonalState): Converter
= Converter(elementToConvert, settings, inConversionScope, services, commonState, state)
private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null) private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
data class IntermediateResult( data class IntermediateResult(
@@ -134,14 +123,14 @@ class Converter private constructor(
var i = 0 var i = 0
while (i < commonState.deferredElements.size) { while (i < commonState.deferredElements.size) {
val deferredElement = commonState.deferredElements[i++] val deferredElement = commonState.deferredElements[i++]
deferredElement.unfold(codeConverter.withConverter(this.withState(deferredElement.converterState))) deferredElement.unfold(codeConverter)
} }
commonState.postUnfoldActions.forEach { it() } commonState.postUnfoldActions.forEach { it() }
} }
fun <TResult : Element> deferredElement(generator: (CodeConverter) -> TResult): DeferredElement<TResult> { fun <TResult : Element> deferredElement(generator: (CodeConverter) -> TResult): DeferredElement<TResult> {
val element = DeferredElement(generator, personalState) val element = DeferredElement(generator)
commonState.deferredElements.add(element) commonState.deferredElements.add(element)
return element return element
} }
@@ -686,7 +675,7 @@ class Converter private constructor(
if (!hasExternalQualifier) { if (!hasExternalQualifier) {
// references to nested classes may need correction // references to nested classes may need correction
if (targetClass != null) { if (targetClass != null) {
val identifier = constructNestedClassReferenceIdentifier(targetClass, specialContext ?: element) val identifier = constructNestedClassReferenceIdentifier(targetClass, element)
if (identifier != null) { if (identifier != null) {
return ReferenceElement(identifier, typeArgs).assignPrototype(element, CommentsAndSpacesInheritance.NO_SPACES) return ReferenceElement(identifier, typeArgs).assignPrototype(element, CommentsAndSpacesInheritance.NO_SPACES)
} }
@@ -701,7 +690,7 @@ class Converter private constructor(
val outerClass = psiClass.containingClass val outerClass = psiClass.containingClass
if (outerClass != null if (outerClass != null
&& !PsiTreeUtil.isAncestor(outerClass, context, true) && !PsiTreeUtil.isAncestor(outerClass, context, true)
&& !psiClass.isImported(context.containingFile as PsiJavaFile)) { && !psiClass.isImported(elementToConvert.containingFile as PsiJavaFile)) {
val qualifier = constructNestedClassReferenceIdentifier(outerClass, context)?.name ?: outerClass.name!! val qualifier = constructNestedClassReferenceIdentifier(outerClass, context)?.name ?: outerClass.name!!
return Identifier.withNoPrototype(Identifier.toKotlin(qualifier) + "." + Identifier.toKotlin(psiClass.name!!)) return Identifier.withNoPrototype(Identifier.toKotlin(qualifier) + "." + Identifier.toKotlin(psiClass.name!!))
} }
@@ -593,12 +593,11 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
} }
// add qualification for static members from base classes and also this works for enum constants in switch // add qualification for static members from base classes and also this works for enum constants in switch
val context = converter.specialContext ?: expression
if (target is PsiMember if (target is PsiMember
&& target.hasModifierProperty(PsiModifier.STATIC) && target.hasModifierProperty(PsiModifier.STATIC)
&& target.containingClass != null && target.containingClass != null
&& !PsiTreeUtil.isAncestor(target.containingClass, context, true) && !PsiTreeUtil.isAncestor(target.containingClass, expression, true)
&& !target.isImported(context.containingFile as PsiJavaFile)) { && !target.isImported(expression.containingFile as PsiJavaFile)) {
var member: PsiMember = target var member: PsiMember = target
var code = Identifier.toKotlin(referenceName) var code = Identifier.toKotlin(referenceName)
while (true) { while (true) {
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.j2k.ast
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.j2k.CodeBuilder import org.jetbrains.kotlin.j2k.CodeBuilder
import org.jetbrains.kotlin.j2k.CodeConverter import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.Converter
import org.jetbrains.kotlin.j2k.EmptyDocCommentConverter import org.jetbrains.kotlin.j2k.EmptyDocCommentConverter
fun <TElement: Element> TElement.assignPrototype(prototype: PsiElement?, inheritance: CommentsAndSpacesInheritance = CommentsAndSpacesInheritance()): TElement { fun <TElement: Element> TElement.assignPrototype(prototype: PsiElement?, inheritance: CommentsAndSpacesInheritance = CommentsAndSpacesInheritance()): TElement {
@@ -109,8 +108,7 @@ abstract class Element {
// this class should never be created directly - Converter.deferredElement() should be used! // this class should never be created directly - Converter.deferredElement() should be used!
class DeferredElement<TResult : Element>( class DeferredElement<TResult : Element>(
private val generator: (CodeConverter) -> TResult, private val generator: (CodeConverter) -> TResult
val converterState: Converter.PersonalState
) : Element() { ) : Element() {
private var result: TResult? = null private var result: TResult? = null
@@ -1,4 +1,4 @@
internal class A @JvmOverloads constructor(nested: A.Nested = A.Nested(A.Nested.FIELD)) { internal class A @JvmOverloads constructor(nested: Nested = Nested(Nested.FIELD)) {
internal class Nested(p: Int) { internal class Nested(p: Int) {
companion object { companion object {
@@ -1,4 +1,4 @@
internal open class Base(nested: Base.Nested) { internal open class Base(nested: Nested) {
internal class Nested(p: Int) { internal class Nested(p: Int) {
companion object { companion object {
+1 -1
View File
@@ -1,4 +1,4 @@
internal class X(private val list: List<X.Y>) { internal class X(private val list: List<Y>) {
internal inner class Y internal inner class Y
} }