J2K: correct conversion of nested class references
#KT-5294 Fixed #KT-5400 Fixed
This commit is contained in:
@@ -183,8 +183,7 @@ class ConstructorConverter(private val psiClass: PsiClass, private val converter
|
||||
var body = postProcessBody(bodyConverter.convertBlock(constructor.getBody()))
|
||||
val containingClass = constructor.getContainingClass()
|
||||
val typeParameterList = converter.convertTypeParameterList(containingClass?.getTypeParameterList())
|
||||
val factoryFunctionType = ClassType(containingClass?.declarationIdentifier() ?: Identifier.Empty,
|
||||
typeParameterList.parameters,
|
||||
val factoryFunctionType = ClassType(ReferenceElement(containingClass?.declarationIdentifier() ?: Identifier.Empty, typeParameterList.parameters).assignNoPrototype(),
|
||||
Nullability.NotNull,
|
||||
converter.settings).assignNoPrototype()
|
||||
return FactoryFunction(constructor.declarationIdentifier(), annotations, correctFactoryFunctionAccess(modifiers),
|
||||
@@ -201,13 +200,14 @@ class ConstructorConverter(private val psiClass: PsiClass, private val converter
|
||||
val body = primaryConstructor.getBody()
|
||||
|
||||
val parameterUsageReplacementMap = HashMap<String, String>()
|
||||
val correctedTypeConverter = converter.withSpecialContext(psiClass).typeConverter /* to correct nested class references */
|
||||
val block = if (body != null) {
|
||||
val statementsToRemove = HashSet<PsiStatement>()
|
||||
for (parameter in params) {
|
||||
val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, primaryConstructor) ?: continue
|
||||
|
||||
val fieldType = typeConverter.convertVariableType(field)
|
||||
val parameterType = typeConverter.convertVariableType(parameter)
|
||||
val fieldType = correctedTypeConverter.convertVariableType(field)
|
||||
val parameterType = correctedTypeConverter.convertVariableType(parameter)
|
||||
// types can be different only in nullability
|
||||
val `type` = if (fieldType == parameterType) {
|
||||
fieldType
|
||||
@@ -244,6 +244,7 @@ class ConstructorConverter(private val psiClass: PsiClass, private val converter
|
||||
|
||||
// we need to replace renamed parameter usages in base class constructor arguments and in default values
|
||||
val correctedConverter = converter.withExpressionVisitor { ReplacingExpressionVisitor(this, parameterUsageReplacementMap, it) }
|
||||
.withSpecialContext(psiClass) /* to correct nested class references */
|
||||
|
||||
val statement = primaryConstructor.getBody()?.getStatements()?.firstOrNull()
|
||||
val methodCall = (statement as? PsiExpressionStatement)?.getExpression() as? PsiMethodCallExpression
|
||||
@@ -259,7 +260,7 @@ class ConstructorConverter(private val psiClass: PsiClass, private val converter
|
||||
else
|
||||
null
|
||||
if (!parameterToField.containsKey(parameter)) {
|
||||
converter.convertParameter(parameter, defaultValue = defaultValue)
|
||||
correctedConverter.convertParameter(parameter, defaultValue = defaultValue)
|
||||
}
|
||||
else {
|
||||
val (field, `type`) = parameterToField[parameter]!!
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.psi.CommonClassNames.*
|
||||
import org.jetbrains.jet.lang.types.expressions.OperatorConventions.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.util.PsiMethodUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
|
||||
public trait ConversionScope {
|
||||
public fun contains(element: PsiElement): Boolean
|
||||
@@ -34,29 +35,53 @@ public class FilesConversionScope(val files: Collection<PsiJavaFile>) : Conversi
|
||||
}
|
||||
|
||||
public class Converter private(val project: Project, val settings: ConverterSettings, val conversionScope: ConversionScope, val state: Converter.State) {
|
||||
private class State(val typeConverter: TypeConverter,
|
||||
val methodReturnType: PsiType?,
|
||||
private class State(val methodReturnType: PsiType?,
|
||||
val expressionVisitorFactory: (Converter) -> ExpressionVisitor,
|
||||
val statementVisitorFactory: (Converter) -> StatementVisitor)
|
||||
val typeConverter: TypeConverter = state.typeConverter
|
||||
val statementVisitorFactory: (Converter) -> StatementVisitor,
|
||||
val specialContext: PsiElement?,
|
||||
val importList: ImportList?,
|
||||
val importsToAdd: MutableCollection<String>?)
|
||||
|
||||
val typeConverter: TypeConverter = TypeConverter(this)
|
||||
|
||||
val methodReturnType: PsiType? = state.methodReturnType
|
||||
val specialContext: PsiElement? = state.specialContext
|
||||
val importNames: Set<String> = state.importList?.imports?.mapTo(HashSet<String>()) { it.name } ?: setOf()
|
||||
val importsToAdd: MutableCollection<String>? = state.importsToAdd
|
||||
|
||||
private val expressionVisitor = state.expressionVisitorFactory(this)
|
||||
private val statementVisitor = state.statementVisitorFactory(this)
|
||||
|
||||
class object {
|
||||
public fun create(project: Project, settings: ConverterSettings, conversionScope: ConversionScope): Converter
|
||||
= Converter(project, settings, conversionScope, State(TypeConverter(settings, conversionScope), null, { ExpressionVisitor(it) }, { StatementVisitor(it) }))
|
||||
public fun create(project: Project, settings: ConverterSettings, conversionScope: ConversionScope): Converter {
|
||||
val state = State(null, { ExpressionVisitor(it) }, { StatementVisitor(it) }, null, null, null)
|
||||
return Converter(project, settings, conversionScope, state)
|
||||
}
|
||||
}
|
||||
|
||||
fun withMethodReturnType(methodReturnType: PsiType?): Converter
|
||||
= Converter(project, settings, conversionScope, State(typeConverter, methodReturnType, state.expressionVisitorFactory, state.statementVisitorFactory))
|
||||
= Converter(project, settings, conversionScope,
|
||||
State(methodReturnType, state.expressionVisitorFactory, state.statementVisitorFactory, state.specialContext, state.importList, state.importsToAdd))
|
||||
|
||||
fun withExpressionVisitor(factory: (Converter) -> ExpressionVisitor): Converter
|
||||
= Converter(project, settings, conversionScope, State(typeConverter, state.methodReturnType, factory, state.statementVisitorFactory))
|
||||
= Converter(project, settings, conversionScope,
|
||||
State(state.methodReturnType, factory, state.statementVisitorFactory, state.specialContext, state.importList, state.importsToAdd))
|
||||
|
||||
fun withStatementVisitor(factory: (Converter) -> StatementVisitor): Converter
|
||||
= Converter(project, settings, conversionScope, State(typeConverter, state.methodReturnType, state.expressionVisitorFactory, factory))
|
||||
= Converter(project, settings, conversionScope,
|
||||
State(state.methodReturnType, state.expressionVisitorFactory, factory, state.specialContext, state.importList, state.importsToAdd))
|
||||
|
||||
fun withSpecialContext(context: PsiElement): Converter
|
||||
= Converter(project, settings, conversionScope,
|
||||
State(state.methodReturnType, state.expressionVisitorFactory, state.statementVisitorFactory, context, state.importList, state.importsToAdd))
|
||||
|
||||
private fun withImportList(importList: ImportList): Converter
|
||||
= Converter(project, settings, conversionScope,
|
||||
State(state.methodReturnType, state.expressionVisitorFactory, state.statementVisitorFactory, state.specialContext, importList, state.importsToAdd))
|
||||
|
||||
private fun withImportsToAdd(importsToAdd: MutableCollection<String>): Converter
|
||||
= Converter(project, settings, conversionScope,
|
||||
State(state.methodReturnType, state.expressionVisitorFactory, state.statementVisitorFactory, state.specialContext, state.importList, importsToAdd))
|
||||
|
||||
public fun elementToKotlin(element: PsiElement): String {
|
||||
val converted = convertTopElement(element) ?: return ""
|
||||
@@ -80,21 +105,22 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
}
|
||||
|
||||
private fun convertFile(javaFile: PsiJavaFile): File {
|
||||
val importsToAdd = LinkedHashSet<String>()
|
||||
var converter = this.withImportsToAdd(importsToAdd)
|
||||
var convertedChildren = javaFile.getChildren().map {
|
||||
if (it is PsiImportList) {
|
||||
val importList = convertImportList(it)
|
||||
typeConverter.importList = importList
|
||||
converter = converter.withImportList(importList)
|
||||
importList
|
||||
}
|
||||
else {
|
||||
convertTopElement(it)
|
||||
converter.convertTopElement(it)
|
||||
}
|
||||
}.filterNotNull()
|
||||
|
||||
typeConverter.importList = null
|
||||
if (typeConverter.importsToAdd.isNotEmpty()) {
|
||||
if (importsToAdd.isNotEmpty()) {
|
||||
val importList = convertedChildren.filterIsInstance(javaClass<ImportList>()).first()
|
||||
val newImportList = ImportList(importList.imports + typeConverter.importsToAdd).assignPrototypesFrom(importList)
|
||||
val newImportList = ImportList(importList.imports + importsToAdd.map { Import(it).assignNoPrototype() }).assignPrototypesFrom(importList)
|
||||
convertedChildren = convertedChildren.map { if (it == importList) newImportList else it }
|
||||
}
|
||||
|
||||
@@ -392,6 +418,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
return expressionVisitor.result.assignPrototype(expression)
|
||||
}
|
||||
|
||||
//TODO: drop this method - it has unclear semantics
|
||||
fun convertElement(element: PsiElement?): Element {
|
||||
if (element == null) return Element.Empty
|
||||
|
||||
@@ -400,6 +427,46 @@ public class Converter private(val project: Project, val settings: ConverterSett
|
||||
return elementVisitor.result.assignPrototype(element)
|
||||
}
|
||||
|
||||
fun convertCodeReferenceElement(element: PsiJavaCodeReferenceElement, hasExternalQualifier: Boolean, typeArgsConverted: List<Element>? = null): ReferenceElement {
|
||||
val typeArgs = typeArgsConverted ?: typeConverter.convertTypes(element.getTypeParameters())
|
||||
|
||||
if (element.isQualified()) {
|
||||
var result = Identifier.toKotlin(element.getReferenceName()!!)
|
||||
var qualifier = element.getQualifier()
|
||||
while (qualifier != null) {
|
||||
val codeRefElement = qualifier as PsiJavaCodeReferenceElement
|
||||
result = Identifier.toKotlin(codeRefElement.getReferenceName()!!) + "." + result
|
||||
qualifier = codeRefElement.getQualifier()
|
||||
}
|
||||
return ReferenceElement(Identifier(result).assignNoPrototype(), typeArgs).assignPrototype(element)
|
||||
}
|
||||
else {
|
||||
if (!hasExternalQualifier) {
|
||||
// references to nested classes may need correction
|
||||
val targetClass = element.resolve() as? PsiClass
|
||||
if (targetClass != null) {
|
||||
val identifier = constructNestedClassReferenceIdentifier(targetClass, specialContext ?: element)
|
||||
if (identifier != null) {
|
||||
return ReferenceElement(identifier, typeArgs).assignPrototype(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReferenceElement(Identifier(element.getReferenceName()!!).assignNoPrototype(), typeArgs).assignPrototype(element)
|
||||
}
|
||||
}
|
||||
|
||||
private fun constructNestedClassReferenceIdentifier(psiClass: PsiClass, context: PsiElement): Identifier? {
|
||||
val outerClass = psiClass.getContainingClass()
|
||||
if (outerClass != null
|
||||
&& !PsiTreeUtil.isAncestor(outerClass, context, true)
|
||||
&& !psiClass.isImported(context.getContainingFile() as PsiJavaFile)) {
|
||||
val qualifier = constructNestedClassReferenceIdentifier(outerClass, context)?.name ?: outerClass.getName()!!
|
||||
return Identifier(Identifier.toKotlin(qualifier) + "." + Identifier.toKotlin(psiClass.getName()!!)).assignNoPrototype()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun convertTypeElement(element: PsiTypeElement?): TypeElement
|
||||
= TypeElement(if (element == null) ErrorType().assignNoPrototype() else typeConverter.convertType(element.getType())).assignPrototype(element)
|
||||
|
||||
|
||||
@@ -31,27 +31,16 @@ import org.jetbrains.jet.j2k.ast.ErrorType
|
||||
import com.intellij.codeInsight.NullableNotNullManager
|
||||
import org.jetbrains.jet.j2k.ast.ArrayType
|
||||
import org.jetbrains.jet.j2k.ast.ClassType
|
||||
import org.jetbrains.jet.j2k.ast.ReferenceElement
|
||||
import org.jetbrains.jet.j2k.ast.Identifier
|
||||
|
||||
class TypeConverter(val settings: ConverterSettings, val conversionScope: ConversionScope) {
|
||||
class TypeConverter(val converter: Converter) {
|
||||
private val nullabilityCache = HashMap<PsiElement, Nullability>()
|
||||
private val classesToImport = HashSet<String>()
|
||||
|
||||
public var importList: ImportList? = null
|
||||
set(value) {
|
||||
$importList = value
|
||||
importNames = importList?.imports?.mapTo(HashSet<String>()) { it.name } ?: setOf()
|
||||
|
||||
}
|
||||
private var importNames: Set<String> = setOf()
|
||||
|
||||
public val importsToAdd: Collection<Import>
|
||||
get() = classesToImport.map { Import(it).assignNoPrototype() }
|
||||
|
||||
public fun convertType(`type`: PsiType?, nullability: Nullability = Nullability.Default): Type {
|
||||
if (`type` == null) return ErrorType().assignNoPrototype()
|
||||
|
||||
val result = `type`.accept<Type>(TypeVisitor(this, importNames, classesToImport))!!.assignNoPrototype()
|
||||
val result = `type`.accept<Type>(TypeVisitor(converter))!!.assignNoPrototype()
|
||||
return when (nullability) {
|
||||
Nullability.NotNull -> result.toNotNullType()
|
||||
Nullability.Nullable -> result.toNullableType()
|
||||
@@ -64,9 +53,9 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
|
||||
|
||||
public fun convertVariableType(variable: PsiVariable): Type {
|
||||
val result = if (variable.isMainMethodParameter()) {
|
||||
ArrayType(ClassType(Identifier("String").assignNoPrototype(), listOf(), Nullability.NotNull, settings).assignNoPrototype(),
|
||||
ArrayType(ClassType(ReferenceElement(Identifier("String").assignNoPrototype(), listOf()).assignNoPrototype(), Nullability.NotNull, converter.settings).assignNoPrototype(),
|
||||
Nullability.NotNull,
|
||||
settings)
|
||||
converter.settings).assignNoPrototype()
|
||||
}
|
||||
else {
|
||||
convertType(variable.getType(), variableNullability(variable))
|
||||
@@ -124,7 +113,7 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
|
||||
return Nullability.NotNull
|
||||
}
|
||||
|
||||
if (!conversionScope.contains(variable)) { // do not analyze usages out of our conversion scope
|
||||
if (!converter.conversionScope.contains(variable)) { // do not analyze usages out of our conversion scope
|
||||
if (variable is PsiParameter) {
|
||||
// Object.equals corresponds to Any.equals which has nullable parameter:
|
||||
val scope = variable.getDeclarationScope()
|
||||
@@ -203,7 +192,7 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
|
||||
return Nullability.Nullable
|
||||
}
|
||||
|
||||
if (!conversionScope.contains(method)) return nullability // do not analyze body and usages of methods out of our conversion scope
|
||||
if (!converter.conversionScope.contains(method)) return nullability // do not analyze body and usages of methods out of our conversion scope
|
||||
|
||||
if (nullability == Nullability.Default) {
|
||||
method.getBody()?.accept(object: JavaRecursiveElementVisitor() {
|
||||
|
||||
@@ -139,3 +139,20 @@ fun PsiMethod.isMainMethod(): Boolean = PsiMethodUtil.isMainMethod(this)
|
||||
|
||||
fun <T: Any> List<T>.singleOrNull2(): T? = if (size == 1) this[0] else null
|
||||
fun <T: Any> Array<T>.singleOrNull2(): T? = if (size == 1) this[0] else null
|
||||
|
||||
fun PsiMember.isImported(file: PsiJavaFile): Boolean {
|
||||
if (this is PsiClass) {
|
||||
val fqName = getQualifiedName()
|
||||
val index = fqName?.lastIndexOf('.') ?: -1
|
||||
val parentName = if (index >= 0) fqName!!.substring(0, index) else null
|
||||
return file.getImportList()?.getAllImportStatements()?.any {
|
||||
it.getImportReference()?.getQualifiedName() == (if (it.isOnDemand()) parentName else fqName)
|
||||
} ?: false
|
||||
}
|
||||
else {
|
||||
return getContainingClass() != null && file.getImportList()?.getImportStaticStatements()?.any {
|
||||
it.resolveTargetClass() == getContainingClass() && (it.isOnDemand() || it.getReferenceName() == getName())
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.jetbrains.jet.j2k.ast
|
||||
import org.jetbrains.jet.j2k.*
|
||||
|
||||
class NewClassExpression(
|
||||
val name: Element,
|
||||
val name: ReferenceElement?,
|
||||
val arguments: List<Expression>,
|
||||
val qualifier: Expression = Expression.Empty,
|
||||
val anonymousClass: AnonymousClassBody? = null
|
||||
@@ -34,7 +34,9 @@ class NewClassExpression(
|
||||
builder.append(qualifier).append(if (qualifier.isNullable) "!!." else ".")
|
||||
}
|
||||
|
||||
builder.append(name)
|
||||
if (name != null) {
|
||||
builder.append(name)
|
||||
}
|
||||
|
||||
if (anonymousClass == null || !anonymousClass.extendsTrait) {
|
||||
builder.append("(").append(arguments, ", ").append(")")
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.jetbrains.jet.j2k.ast
|
||||
|
||||
import org.jetbrains.jet.j2k.*
|
||||
|
||||
class ReferenceElement(val reference: Identifier, val types: List<Type>) : Element() {
|
||||
class ReferenceElement(val name: Identifier, val typeArgs: List<Element>) : Element() {
|
||||
override fun generateCode(builder: CodeBuilder) {
|
||||
builder.append(reference).append(types, ", ", "<", ">")
|
||||
builder.append(name).append(typeArgs, ", ", "<", ">")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,15 +78,15 @@ class ErrorType : NotNullType() {
|
||||
}
|
||||
}
|
||||
|
||||
class ClassType(val name: Identifier, val typeArgs: List<Element>, nullability: Nullability, settings: ConverterSettings)
|
||||
class ClassType(val referenceElement: ReferenceElement, nullability: Nullability, settings: ConverterSettings)
|
||||
: MayBeNullableType(nullability, settings) {
|
||||
|
||||
override fun generateCode(builder: CodeBuilder) {
|
||||
builder.append(name).append(typeArgs, ", ", "<", ">").append(isNullableStr)
|
||||
builder append referenceElement append isNullableStr
|
||||
}
|
||||
|
||||
override fun toNotNullType(): Type = ClassType(name, typeArgs, Nullability.NotNull, settings).assignPrototypesFrom(this)
|
||||
override fun toNullableType(): Type = ClassType(name, typeArgs, Nullability.Nullable, settings).assignPrototypesFrom(this)
|
||||
override fun toNotNullType(): Type = ClassType(referenceElement, Nullability.NotNull, settings).assignPrototypesFrom(this)
|
||||
override fun toNullableType(): Type = ClassType(referenceElement, Nullability.Nullable, settings).assignPrototypesFrom(this)
|
||||
}
|
||||
|
||||
class ArrayType(val elementType: Type, nullability: Nullability, settings: ConverterSettings)
|
||||
|
||||
@@ -42,23 +42,6 @@ class ElementVisitor(private val converter: Converter) : JavaElementVisitor() {
|
||||
result = ExpressionList(converter.convertExpressions(list.getExpressions()))
|
||||
}
|
||||
|
||||
override fun visitReferenceElement(reference: PsiJavaCodeReferenceElement) {
|
||||
val types = typeConverter.convertTypes(reference.getTypeParameters())
|
||||
if (!reference.isQualified()) {
|
||||
result = ReferenceElement(Identifier(reference.getReferenceName()!!).assignNoPrototype(), types)
|
||||
}
|
||||
else {
|
||||
var code = Identifier.toKotlin(reference.getReferenceName()!!)
|
||||
var qualifier = reference.getQualifier()
|
||||
while (qualifier != null) {
|
||||
val p = qualifier as PsiJavaCodeReferenceElement
|
||||
code = Identifier.toKotlin(p.getReferenceName()!!) + "." + code
|
||||
qualifier = p.getQualifier()
|
||||
}
|
||||
result = ReferenceElement(Identifier(code).assignNoPrototype(), types)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeElement(`type`: PsiTypeElement) {
|
||||
result = TypeElement(typeConverter.convertType(`type`.getType()))
|
||||
}
|
||||
|
||||
@@ -254,19 +254,17 @@ open class ExpressionVisitor(private val converter: Converter) : JavaElementVisi
|
||||
converter.convertExpressions(expression.getArrayDimensions()))
|
||||
}
|
||||
else {
|
||||
result = createNewClassExpression(expression)
|
||||
val anonymousClass = expression.getAnonymousClass()
|
||||
val qualifier = expression.getQualifier()
|
||||
val classRef = expression.getClassOrAnonymousClassReference()
|
||||
val classRefConverted = if (classRef != null) converter.convertCodeReferenceElement(classRef, hasExternalQualifier = qualifier != null) else null
|
||||
result = NewClassExpression(classRefConverted,
|
||||
convertArguments(expression),
|
||||
converter.convertExpression(qualifier),
|
||||
if (anonymousClass != null) converter.convertAnonymousClassBody(anonymousClass) else null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNewClassExpression(expression: PsiNewExpression): Expression {
|
||||
val anonymousClass = expression.getAnonymousClass()
|
||||
val classReference = expression.getClassOrAnonymousClassReference()
|
||||
return NewClassExpression(converter.convertElement(classReference),
|
||||
convertArguments(expression),
|
||||
converter.convertExpression(expression.getQualifier()),
|
||||
if (anonymousClass != null) converter.convertAnonymousClassBody(anonymousClass) else null)
|
||||
}
|
||||
|
||||
override fun visitParenthesizedExpression(expression: PsiParenthesizedExpression) {
|
||||
result = ParenthesizedExpression(converter.convertExpression(expression.getExpression()))
|
||||
}
|
||||
@@ -317,16 +315,18 @@ open class ExpressionVisitor(private val converter: Converter) : JavaElementVisi
|
||||
}
|
||||
|
||||
// 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
|
||||
&& target.hasModifierProperty(PsiModifier.STATIC)
|
||||
&& target.getContainingClass() != null
|
||||
&& !PsiTreeUtil.isAncestor(target.getContainingClass(), expression, true)
|
||||
&& !isStaticallyImported(target, expression)) {
|
||||
&& !PsiTreeUtil.isAncestor(target.getContainingClass(), context, true)
|
||||
&& !target.isImported(context.getContainingFile() as PsiJavaFile)) {
|
||||
var member: PsiMember = target
|
||||
var code = Identifier.toKotlin(referenceName)
|
||||
while (member.getContainingClass() != null) {
|
||||
code = Identifier.toKotlin(member.getContainingClass()!!.getName()!!) + "." + code
|
||||
member = member.getContainingClass()!!
|
||||
while (true) {
|
||||
val containingClass = member.getContainingClass() ?: break
|
||||
code = Identifier.toKotlin(containingClass.getName()!!) + "." + code
|
||||
member = containingClass
|
||||
}
|
||||
result = Identifier(code, false, false)
|
||||
return
|
||||
@@ -433,23 +433,4 @@ open class ExpressionVisitor(private val converter: Converter) : JavaElementVisi
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
private fun isStaticallyImported(member: PsiMember, context: PsiElement): Boolean {
|
||||
val containingFile = context.getContainingFile()
|
||||
val targetContainingClass = member.getContainingClass()
|
||||
if (containingFile is PsiJavaFile && targetContainingClass != null) {
|
||||
val importList = containingFile.getImportList();
|
||||
if (importList != null) {
|
||||
return importList.getImportStaticStatements().any { importResolvesTo(it, member) }
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun importResolvesTo(importStatement: PsiImportStaticStatement, member: PsiMember): Boolean {
|
||||
val targetContainingClass = member.getContainingClass()
|
||||
val importedClass = importStatement.resolveTargetClass()
|
||||
return importedClass == targetContainingClass
|
||||
&& (importStatement.isOnDemand() || importStatement.getReferenceName() == member.getName())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,19 @@ package org.jetbrains.jet.j2k.visitors
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.PsiClassReferenceType
|
||||
import org.jetbrains.jet.j2k.ast.*
|
||||
import java.util.LinkedList
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType
|
||||
import org.jetbrains.jet.j2k.TypeConverter
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.j2k.singleOrNull2
|
||||
import org.jetbrains.jet.j2k.Converter
|
||||
|
||||
private val PRIMITIVE_TYPES_NAMES = JvmPrimitiveType.values().map { it.getName() }
|
||||
|
||||
class TypeVisitor(private val converter: TypeConverter, private val importNames: Set<String>, private val classesToImport: MutableSet<String>) : PsiTypeVisitor<Type>() {
|
||||
class TypeVisitor(private val converter: Converter) : PsiTypeVisitor<Type>() {
|
||||
|
||||
private val typeConverter: TypeConverter = converter.typeConverter
|
||||
|
||||
override fun visitPrimitiveType(primitiveType: PsiPrimitiveType): Type {
|
||||
val name = primitiveType.getCanonicalText()
|
||||
return if (name == "void") {
|
||||
@@ -45,100 +49,86 @@ class TypeVisitor(private val converter: TypeConverter, private val importNames:
|
||||
}
|
||||
|
||||
override fun visitArrayType(arrayType: PsiArrayType): Type {
|
||||
return ArrayType(converter.convertType(arrayType.getComponentType()), Nullability.Default, converter.settings)
|
||||
return ArrayType(typeConverter.convertType(arrayType.getComponentType()), Nullability.Default, converter.settings)
|
||||
}
|
||||
|
||||
override fun visitClassType(classType: PsiClassType): Type {
|
||||
val identifier = constructClassTypeIdentifier(classType)
|
||||
val resolvedClassTypeParams = createRawTypesForResolvedReference(classType)
|
||||
if (classType.getParameterCount() == 0 && resolvedClassTypeParams.size() > 0) {
|
||||
val starParamList = ArrayList<Type>()
|
||||
if (resolvedClassTypeParams.size() == 1) {
|
||||
if ((resolvedClassTypeParams.single() as ClassType).name.name == "Any") {
|
||||
starParamList.add(StarProjectionType())
|
||||
return ClassType(identifier, starParamList, Nullability.Default, converter.settings)
|
||||
}
|
||||
else {
|
||||
return ClassType(identifier, resolvedClassTypeParams, Nullability.Default, converter.settings)
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ClassType(identifier, resolvedClassTypeParams, Nullability.Default, converter.settings)
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ClassType(identifier, converter.convertTypes(classType.getParameters()), Nullability.Default, converter.settings)
|
||||
}
|
||||
val refElement = constructReferenceElement(classType)
|
||||
return ClassType(refElement, Nullability.Default, converter.settings)
|
||||
}
|
||||
|
||||
private fun constructClassTypeIdentifier(classType: PsiClassType): Identifier {
|
||||
private fun constructReferenceElement(classType: PsiClassType): ReferenceElement {
|
||||
val typeArgs = convertTypeArgs(classType)
|
||||
|
||||
val psiClass = classType.resolve()
|
||||
if (psiClass != null) {
|
||||
val javaClassName = psiClass.getQualifiedName()
|
||||
val kotlinClassName = toKotlinTypesMap[javaClassName]
|
||||
if (kotlinClassName != null) {
|
||||
val kotlinShortName = getShortName(kotlinClassName)
|
||||
if (kotlinShortName == getShortName(javaClassName!!) && importNames.contains(getPackageName(javaClassName) + ".*")) {
|
||||
classesToImport.add(kotlinClassName)
|
||||
if (kotlinShortName == getShortName(javaClassName!!) && converter.importNames.contains(getPackageName(javaClassName) + ".*")) {
|
||||
converter.importsToAdd?.add(kotlinClassName)
|
||||
}
|
||||
return Identifier(kotlinShortName).assignNoPrototype()
|
||||
return ReferenceElement(Identifier(kotlinShortName).assignNoPrototype(), typeArgs).assignNoPrototype()
|
||||
}
|
||||
}
|
||||
|
||||
if (classType is PsiClassReferenceType) {
|
||||
val reference = classType.getReference()
|
||||
if (reference.isQualified()) {
|
||||
var result = Identifier.toKotlin(reference.getReferenceName()!!)
|
||||
var qualifier = reference.getQualifier()
|
||||
while (qualifier != null) {
|
||||
val codeRefElement = qualifier as PsiJavaCodeReferenceElement
|
||||
result = Identifier.toKotlin(codeRefElement.getReferenceName()!!) + "." + result
|
||||
qualifier = codeRefElement.getQualifier()
|
||||
}
|
||||
return Identifier(result).assignNoPrototype()
|
||||
}
|
||||
return converter.convertCodeReferenceElement(classType.getReference(), hasExternalQualifier = false, typeArgsConverted = typeArgs)
|
||||
}
|
||||
|
||||
return Identifier(classType.getClassName() ?: "").assignNoPrototype()
|
||||
return ReferenceElement(Identifier(classType.getClassName() ?: "").assignNoPrototype(), typeArgs).assignNoPrototype()
|
||||
}
|
||||
|
||||
private fun getPackageName(className: String): String = className.substring(0, className.lastIndexOf('.'))
|
||||
private fun getShortName(className: String): String = className.substring(className.lastIndexOf('.') + 1)
|
||||
|
||||
private fun convertTypeArgs(classType: PsiClassType): List<Type> {
|
||||
val resolvedClassTypeParams = createRawTypesForResolvedReference(classType)
|
||||
|
||||
if (classType.getParameterCount() == 0 && resolvedClassTypeParams.size() > 0) {
|
||||
if ((resolvedClassTypeParams.singleOrNull2() as? ClassType)?.referenceElement?.name?.name == "Any") {
|
||||
return listOf(StarProjectionType().assignNoPrototype())
|
||||
}
|
||||
else {
|
||||
return resolvedClassTypeParams
|
||||
}
|
||||
}
|
||||
else {
|
||||
return typeConverter.convertTypes(classType.getParameters())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRawTypesForResolvedReference(classType: PsiClassType): List<Type> {
|
||||
val typeParams = LinkedList<Type>()
|
||||
val typeArgs = ArrayList<Type>()
|
||||
if (classType is PsiClassReferenceType) {
|
||||
val resolve = classType.getReference().resolve()
|
||||
if (resolve is PsiClass) {
|
||||
for (typeParam in resolve.getTypeParameters()) {
|
||||
val superTypes = typeParam.getSuperTypes()
|
||||
val boundType = if (superTypes.size > 0) {
|
||||
ClassType(constructClassTypeIdentifier(superTypes[0]),
|
||||
converter.convertTypes(superTypes[0].getParameters()),
|
||||
Nullability.Default,
|
||||
converter.settings)
|
||||
ClassType(constructReferenceElement(superTypes.first()), Nullability.Default, converter.settings)
|
||||
}
|
||||
else {
|
||||
StarProjectionType()
|
||||
}
|
||||
typeParams.add(boundType)
|
||||
typeArgs.add(boundType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return typeParams
|
||||
return typeArgs
|
||||
}
|
||||
|
||||
override fun visitWildcardType(wildcardType: PsiWildcardType): Type {
|
||||
return when {
|
||||
wildcardType.isExtends() -> OutProjectionType(converter.convertType(wildcardType.getExtendsBound()))
|
||||
wildcardType.isSuper() -> InProjectionType(converter.convertType(wildcardType.getSuperBound()))
|
||||
wildcardType.isExtends() -> OutProjectionType(typeConverter.convertType(wildcardType.getExtendsBound()))
|
||||
wildcardType.isSuper() -> InProjectionType(typeConverter.convertType(wildcardType.getSuperBound()))
|
||||
else -> StarProjectionType()
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitEllipsisType(ellipsisType: PsiEllipsisType): Type {
|
||||
return VarArgType(converter.convertType(ellipsisType.getComponentType()))
|
||||
return VarArgType(typeConverter.convertType(ellipsisType.getComponentType()))
|
||||
}
|
||||
|
||||
class object {
|
||||
|
||||
@@ -891,6 +891,31 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
||||
doTest("j2k/tests/testData/ast/constructors/methodCallInFactoryFun.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassNameInParameterDefaults.java")
|
||||
public void testNestedClassNameInParameterDefaults() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/constructors/nestedClassNameInParameterDefaults.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassNameInParameterDefaults2.java")
|
||||
public void testNestedClassNameInParameterDefaults2() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/constructors/nestedClassNameInParameterDefaults2.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassNameInParameterDefaults3.java")
|
||||
public void testNestedClassNameInParameterDefaults3() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/constructors/nestedClassNameInParameterDefaults3.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassNameInParameterDefaults4.java")
|
||||
public void testNestedClassNameInParameterDefaults4() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/constructors/nestedClassNameInParameterDefaults4.java");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassNameInSuperParameters.java")
|
||||
public void testNestedClassNameInSuperParameters() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/constructors/nestedClassNameInSuperParameters.java");
|
||||
}
|
||||
|
||||
@TestMetadata("noPrimary.java")
|
||||
public void testNoPrimary() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/constructors/noPrimary.java");
|
||||
@@ -1732,6 +1757,16 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
||||
doTest("j2k/tests/testData/ast/issues/kt-1074.java");
|
||||
}
|
||||
|
||||
@TestMetadata("kt-5294.java")
|
||||
public void testKt_5294() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/issues/kt-5294.java");
|
||||
}
|
||||
|
||||
@TestMetadata("kt-5400.java")
|
||||
public void testKt_5400() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/issues/kt-5400.java");
|
||||
}
|
||||
|
||||
@TestMetadata("kt-543.java")
|
||||
public void testKt_543() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/issues/kt-543.java");
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
class A {
|
||||
A(Nested nested) {
|
||||
}
|
||||
|
||||
A() {
|
||||
this(new Nested(Nested.FIELD));
|
||||
}
|
||||
|
||||
static class Nested {
|
||||
Nested(int p){}
|
||||
|
||||
public static final int FIELD = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class A(nested: A.Nested = A.Nested(A.Nested.FIELD)) {
|
||||
|
||||
class Nested(p: Int) {
|
||||
class object {
|
||||
|
||||
public val FIELD: Int = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import A.Nested;
|
||||
|
||||
class A {
|
||||
A(Nested nested) {
|
||||
}
|
||||
|
||||
A() {
|
||||
this(new Nested(Nested.FIELD));
|
||||
}
|
||||
|
||||
static class Nested {
|
||||
Nested(int p){}
|
||||
|
||||
public static final int FIELD = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
Nested nested;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import A.Nested
|
||||
|
||||
class A(nested: Nested = Nested(Nested.FIELD)) {
|
||||
|
||||
class Nested(p: Int) {
|
||||
class object {
|
||||
|
||||
public val FIELD: Int = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
var nested: Nested
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package pack;
|
||||
|
||||
import static pack.A.Nested;
|
||||
|
||||
class A {
|
||||
A(Nested nested) {
|
||||
}
|
||||
|
||||
A() {
|
||||
this(new Nested(Nested.FIELD));
|
||||
}
|
||||
|
||||
static class Nested {
|
||||
Nested(int p){}
|
||||
|
||||
public static final int FIELD = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
Nested nested;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package pack
|
||||
|
||||
import pack.A.Nested
|
||||
|
||||
class A(nested: Nested = Nested(Nested.FIELD)) {
|
||||
|
||||
class Nested(p: Int) {
|
||||
class object {
|
||||
|
||||
public val FIELD: Int = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
var nested: Nested
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package pack;
|
||||
|
||||
import static pack.A.*;
|
||||
|
||||
class A {
|
||||
A(Nested nested) {
|
||||
}
|
||||
|
||||
A() {
|
||||
this(new Nested(Nested.FIELD));
|
||||
}
|
||||
|
||||
static class Nested {
|
||||
Nested(int p){}
|
||||
|
||||
public static final int FIELD = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
Nested nested;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package pack
|
||||
|
||||
import pack.A.*
|
||||
|
||||
class A(nested: Nested = Nested(Nested.FIELD)) {
|
||||
|
||||
class Nested(p: Int) {
|
||||
class object {
|
||||
|
||||
public val FIELD: Int = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
var nested: Nested
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class Base {
|
||||
Base(Nested nested){}
|
||||
|
||||
static class Nested {
|
||||
Nested(int p){}
|
||||
|
||||
public static final int FIELD = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Derived extends Base {
|
||||
Derived() {
|
||||
super(new Nested(Nested.FIELD));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class Base(nested: Base.Nested) {
|
||||
|
||||
class Nested(p: Int) {
|
||||
class object {
|
||||
|
||||
public val FIELD: Int = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Derived : Base(Base.Nested(Base.Nested.FIELD))
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.List;
|
||||
|
||||
class X {
|
||||
private final List<Y> list;
|
||||
|
||||
X(List<Y> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
class Y{}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class X(private val list: List<X.Y>) {
|
||||
|
||||
inner class Y
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Base {
|
||||
class Nested{}
|
||||
}
|
||||
|
||||
class Derived extends Base {
|
||||
Nested field;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Base {
|
||||
inner class Nested
|
||||
}
|
||||
|
||||
class Derived : Base() {
|
||||
var field: Base.Nested
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import java.util.*
|
||||
import kotlin.List
|
||||
import kotlin.Iterator
|
||||
import kotlin.List
|
||||
|
||||
trait I<T : List<Iterator<String>>>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user