Fixed KT-5434 J2K: incorrect conversion of main method

#KT-5434 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-07-08 21:34:46 +04:00
parent 28e003e79f
commit 2ebaab867d
14 changed files with 83 additions and 57 deletions
@@ -16,80 +16,52 @@
package org.jetbrains.jet.j2k package org.jetbrains.jet.j2k
import java.text.MessageFormat
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiJavaFile
import java.util.ArrayList import java.util.ArrayList
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiAnonymousClass import com.intellij.psi.PsiAnonymousClass
import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiType import com.intellij.psi.PsiType
import com.intellij.psi.PsiArrayType import com.intellij.psi.PsiArrayType
fun createMainFunction(file: PsiFile): String { fun createMainFunction(file: PsiJavaFile): String {
val classNamesWithMains = ArrayList<Pair<String, PsiMethod>>() val classNamesWithMains = ArrayList<Pair<String, PsiMethod>>()
for (c in (file as PsiJavaFile).getClasses()) { for (c in file.getClasses()) {
val main = findMainMethod(c) val main = findMainMethod(c)
val name = c.getName() val name = c.getName()
if (name != null && main != null) { if (name != null && main != null) {
classNamesWithMains.add(Pair(name, main)) classNamesWithMains.add(name to main)
} }
} }
if (classNamesWithMains.size() > 0) { if (classNamesWithMains.isNotEmpty()) {
var className = classNamesWithMains[0].first var className = classNamesWithMains.first().first
return MessageFormat.format("fun main(args : Array<String>) = {0}.main(args as Array<String?>?)", className) return "fun main(args : Array<String>) = $className.main(args)"
} }
return "" return ""
} }
private fun findMainMethod(aClass: PsiClass): PsiMethod? { private fun findMainMethod(aClass: PsiClass): PsiMethod?
if (isMainClass(aClass)) { = if (isMainClass(aClass)) aClass.findMethodsByName("main", false).firstOrNull { it.isMainMethod() } else null
return findMainMethod(aClass.findMethodsByName("main", false))
}
return null
}
private fun isMainClass(psiClass: PsiClass): Boolean { private fun isMainClass(psiClass: PsiClass): Boolean
if (psiClass is PsiAnonymousClass) = psiClass !is PsiAnonymousClass &&
return false !psiClass.isInterface() &&
(psiClass.getContainingClass() == null || psiClass.hasModifierProperty(PsiModifier.STATIC))
if (psiClass.isInterface()) fun PsiMethod.isMainMethod(): Boolean {
return false if (getReturnType() != PsiType.VOID) return false
if (!hasModifierProperty(PsiModifier.STATIC)) return false
if (!hasModifierProperty(PsiModifier.PUBLIC)) return false
return psiClass.getContainingClass() == null || psiClass.hasModifierProperty(PsiModifier.STATIC) val parameters = getParameterList().getParameters()
if (parameters.size != 1) return false
} val `type` = parameters.single().getType()
if (`type` !is PsiArrayType) return false
private fun findMainMethod(mainMethods: Array<PsiMethod>): PsiMethod? {
return mainMethods.find { isMainMethod(it) }
}
fun isMainMethod(method: PsiMethod): Boolean {
if (method.getContainingClass() == null)
return false
if (PsiType.VOID != method.getReturnType())
return false
if (!method.hasModifierProperty(PsiModifier.STATIC))
return false
if (!method.hasModifierProperty(PsiModifier.PUBLIC))
return false
val parameters = method.getParameterList().getParameters()
if (parameters.size != 1)
return false
val `type` = parameters[0].getType()
if (`type` !is PsiArrayType)
return false
val componentType = `type`.getComponentType() val componentType = `type`.getComponentType()
return componentType.equalsToText("java.lang.String") return componentType.equalsToText("java.lang.String")
} }
+20 -2
View File
@@ -29,6 +29,9 @@ import com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT
import org.jetbrains.jet.j2k.ast.assignNoPrototype import org.jetbrains.jet.j2k.ast.assignNoPrototype
import org.jetbrains.jet.j2k.ast.ErrorType import org.jetbrains.jet.j2k.ast.ErrorType
import com.intellij.codeInsight.NullableNotNullManager 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.Identifier
class TypeConverter(val settings: ConverterSettings, val conversionScope: ConversionScope) { class TypeConverter(val settings: ConverterSettings, val conversionScope: ConversionScope) {
private val nullabilityCache = HashMap<PsiElement, Nullability>() private val nullabilityCache = HashMap<PsiElement, Nullability>()
@@ -59,8 +62,17 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
public fun convertTypes(types: Array<PsiType>): List<Type> public fun convertTypes(types: Array<PsiType>): List<Type>
= types.map { convertType(it) } = types.map { convertType(it) }
public fun convertVariableType(variable: PsiVariable): Type public fun convertVariableType(variable: PsiVariable): Type {
= convertType(variable.getType(), variableNullability(variable)).assignPrototype(variable.getTypeElement()) val result = if (variable.isMainMethodParameter()) {
ArrayType(ClassType(Identifier("String").assignNoPrototype(), listOf(), Nullability.NotNull, settings).assignNoPrototype(),
Nullability.NotNull,
settings)
}
else {
convertType(variable.getType(), variableNullability(variable))
}
return result.assignPrototype(variable.getTypeElement())
}
public fun variableNullability(variable: PsiVariable): Nullability { public fun variableNullability(variable: PsiVariable): Nullability {
val cached = nullabilityCache[variable] val cached = nullabilityCache[variable]
@@ -108,6 +120,10 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
return Nullability.Nullable return Nullability.Nullable
} }
if (nullability == Nullability.Default && variable.isMainMethodParameter() ) {
return Nullability.NotNull
}
if (!conversionScope.contains(variable)) { // do not analyze usages out of our conversion scope if (!conversionScope.contains(variable)) { // do not analyze usages out of our conversion scope
if (variable is PsiParameter) { if (variable is PsiParameter) {
// Object.equals corresponds to Any.equals which has nullable parameter: // Object.equals corresponds to Any.equals which has nullable parameter:
@@ -158,6 +174,8 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
return nullability return nullability
} }
private fun PsiVariable.isMainMethodParameter() = this is PsiParameter && (getDeclarationScope() as? PsiMethod)?.isMainMethod() ?: false
public fun convertMethodReturnType(method: PsiMethod): Type public fun convertMethodReturnType(method: PsiMethod): Type
= convertType(method.getReturnType(), methodNullability(method)).assignPrototype(method.getReturnTypeElement()) = convertType(method.getReturnType(), methodNullability(method)).assignPrototype(method.getReturnTypeElement())
@@ -1391,6 +1391,16 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
doTest("j2k/tests/testData/ast/function/main.java"); doTest("j2k/tests/testData/ast/function/main.java");
} }
@TestMetadata("main2.java")
public void testMain2() throws Exception {
doTest("j2k/tests/testData/ast/function/main2.java");
}
@TestMetadata("mainAndNullabilitySetting.java")
public void testMainAndNullabilitySetting() throws Exception {
doTest("j2k/tests/testData/ast/function/mainAndNullabilitySetting.java");
}
@TestMetadata("methodClassType.java") @TestMetadata("methodClassType.java")
public void testMethodClassType() throws Exception { public void testMethodClassType() throws Exception {
doTest("j2k/tests/testData/ast/function/methodClassType.java"); doTest("j2k/tests/testData/ast/function/methodClassType.java");
@@ -0,0 +1,5 @@
//file
public class A {
public static void main(String[] args) {
}
}
+7
View File
@@ -0,0 +1,7 @@
public class A {
class object {
public fun main(args: Array<String>) {
}
}
}
fun main(args: Array<String>) = A.main(args)
@@ -0,0 +1,6 @@
// !forceNotNullTypes: false
//file
public class A {
public static void main(String[] args) {
}
}
@@ -0,0 +1,8 @@
// !forceNotNullTypes: false
public class A {
class object {
public fun main(args: Array<String>) {
}
}
}
fun main(args: Array<String>) = A.main(args)
+1 -1
View File
@@ -31,4 +31,4 @@ public class User {
} }
} }
} }
fun main(args: Array<String>) = User.main(args as Array<String?>?) fun main(args: Array<String>) = User.main(args)
+1 -1
View File
@@ -7,4 +7,4 @@ class Program {
} }
} }
} }
fun main(args: Array<String>) = Program.main(args as Array<String?>?) fun main(args: Array<String>) = Program.main(args)
@@ -83,4 +83,4 @@ public class SwitchDemo {
} }
} }
} }
fun main(args: Array<String>) = SwitchDemo.main(args as Array<String?>?) fun main(args: Array<String>) = SwitchDemo.main(args)
+1 -1
View File
@@ -11,4 +11,4 @@ public class NonDefault {
} }
} }
} }
fun main(args: Array<String>) = NonDefault.main(args as Array<String?>?) fun main(args: Array<String>) = NonDefault.main(args)
+1 -1
View File
@@ -13,4 +13,4 @@ public class SwitchDemo {
} }
} }
} }
fun main(args: Array<String>) = SwitchDemo.main(args as Array<String?>?) fun main(args: Array<String>) = SwitchDemo.main(args)
+1 -1
View File
@@ -25,4 +25,4 @@ public class SwitchDemo {
} }
} }
} }
fun main(args: Array<String>) = SwitchDemo.main(args as Array<String?>?) fun main(args: Array<String>) = SwitchDemo.main(args)
+1 -1
View File
@@ -13,4 +13,4 @@ public class NonDefault {
} }
} }
} }
fun main(args: Array<String>) = NonDefault.main(args as Array<String?>?) fun main(args: Array<String>) = NonDefault.main(args)