J2K: Use parameters nullability for method call

This commit is contained in:
Natalia Ukhorskaya
2016-03-02 14:46:07 +03:00
parent c1df0e4aeb
commit b50f2dc6e5
10 changed files with 131 additions and 24 deletions
+2 -1
View File
@@ -11,4 +11,5 @@
- Protected members used outside of inheritors are converted as public
- Support conversion for annotation constructor calls
- Place comments from the middle of the call to the end
- Drop line breaks between operator arguments (except '+', "-", "&&" and "||")
- Drop line breaks between operator arguments (except '+', "-", "&&" and "||")
- Add non-null assertions on call site for non-null parameters
@@ -87,10 +87,15 @@ class CodeConverter(
isVal).assignPrototype(variable)
}
fun convertExpression(expression: PsiExpression?, expectedType: PsiType?): Expression {
fun convertExpression(expression: PsiExpression?, expectedType: PsiType?, expectedNullability: Nullability? = null): Expression {
if (expression == null) return Identifier.Empty
var convertedExpression = convertExpression(expression)
if (convertedExpression.isNullable && expectedNullability != null && !expectedNullability.isNullable(settings)) {
convertedExpression = BangBangExpression.surroundIfNullable(convertedExpression)
}
if (expectedType == null || expectedType == PsiType.VOID) return convertedExpression
val actualType = expression.type ?: return convertedExpression
@@ -114,21 +114,19 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val operationTokenType = expression.operationTokenType
val expectedNullability = if (operationTokenType in NON_NULL_OPERAND_OPS) Nullability.NotNull else null
val leftOperandExpectedType = getOperandExpectedType(left, right, operationTokenType)
var leftConverted = codeConverter.convertExpression(left, leftOperandExpectedType)
var leftConverted = codeConverter.convertExpression(left, leftOperandExpectedType, expectedNullability)
var rightConverted = codeConverter.convertExpression(
right,
if (leftOperandExpectedType == null)
getOperandExpectedType(right, left, operationTokenType)
else
null
null,
expectedNullability
)
if (operationTokenType in NON_NULL_OPERAND_OPS) {
leftConverted = BangBangExpression.surroundIfNullable(leftConverted)
rightConverted = BangBangExpression.surroundIfNullable(rightConverted)
}
if (operationTokenType == JavaTokenType.GTGTGT) {
result = MethodCallExpression.buildNotNull(leftConverted, "ushr", listOf(rightConverted))
}
@@ -635,15 +633,16 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
val resolved = expression.resolveMethod()
val parameters = resolved?.parameterList?.parameters
val expectedTypes = parameters?.map { it.type } ?: listOf()
val parameters = resolved?.parameterList?.parameters ?: arrayOf()
val commentsAndSpacesInheritance = CommentsAndSpacesInheritance.LINE_BREAKS
return if (arguments.size == expectedTypes.size) {
return if (arguments.size == parameters.size) {
arguments.mapIndexed { i, argument ->
val converted = codeConverter.convertExpression(argument, expectedTypes[i])
val result = if (parameters != null && i == arguments.lastIndex && parameters[i].isVarArgs && argument.type is PsiArrayType)
val expectedNullability = typeConverter.variableNullability(parameters[i])
val converted = codeConverter.convertExpression(argument, parameters[i].type, expectedNullability)
val result = if (i == arguments.lastIndex && parameters[i].isVarArgs && argument.type is PsiArrayType)
StarExpression(converted)
else
converted
@@ -414,7 +414,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
}
protected fun convertWithChangedName(name: String, qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= MethodCallExpression.buildNotNull(codeConverter.convertExpression(qualifier), name, codeConverter.convertExpressions(arguments), typeArgumentsConverted)
= MethodCallExpression.buildNotNull(codeConverter.convertExpression(qualifier), name, arguments.map { codeConverter.convertExpression(it, null, Nullability.NotNull) }, typeArgumentsConverted)
protected fun convertMethodCallWithReceiverCast(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): MethodCallExpression? {
val convertedArguments = arguments.map { codeConverter.convertExpression(it) }
@@ -475,7 +475,7 @@ private fun addIgnoreCaseArgument(
val ignoreCaseExpression = ignoreCaseArgument?.let { codeConverter.convertExpression(it) } ?: LiteralExpression("true").assignNoPrototype()
val ignoreCaseArgumentExpression = AssignmentExpression(Identifier("ignoreCase").assignNoPrototype(), ignoreCaseExpression, Operator.EQ).assignNoPrototype()
return MethodCallExpression.build(codeConverter.convertExpression(qualifier), methodName,
codeConverter.convertExpressions(arguments) + ignoreCaseArgumentExpression,
arguments.map { codeConverter.convertExpression(it, null, Nullability.NotNull) } + ignoreCaseArgumentExpression,
typeArgumentsConverted, false)
}
@@ -119,8 +119,7 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
}
override fun visitForeachStatement(statement: PsiForeachStatement) {
val iteratorExpr = codeConverter.convertExpression(statement.iteratedValue)
val iterator = BangBangExpression.surroundIfNullable(iteratorExpr)
val iterator = codeConverter.convertExpression(statement.iteratedValue, null, Nullability.NotNull)
val iterationParameter = statement.iterationParameter
result = ForeachStatement(iterationParameter.declarationIdentifier(),
if (codeConverter.settings.specifyLocalVariableTypeByDefault) codeConverter.typeConverter.convertVariableType(iterationParameter) else null,
@@ -72,13 +72,13 @@ class TypeConverter(val converter: Converter) {
= convertType(method.returnType, methodNullability(method), methodMutability(method)).assignPrototype(method.returnTypeElement)
fun variableNullability(variable: PsiVariable): Nullability
= nullabilityFlavor.forVariableType(variable)
= nullabilityFlavor.forVariableType(variable, true)
fun methodNullability(method: PsiMethod): Nullability
= nullabilityFlavor.forMethodReturnType(method)
fun variableMutability(variable: PsiVariable): Mutability
= mutabilityFlavor.forVariableType(variable)
= mutabilityFlavor.forVariableType(variable, true)
fun methodMutability(method: PsiMethod): Mutability
= mutabilityFlavor.forMethodReturnType(method)
@@ -117,15 +117,15 @@ class TypeConverter(val converter: Converter) {
open fun forVariableTypeAfterUsageSearch(variable: PsiVariable): T = default
open fun fromMethodBody(body: PsiCodeBlock): T = default
fun forVariableType(variable: PsiVariable): T {
fun forVariableType(variable: PsiVariable, checkScope: Boolean): T {
val cached = cache[variable]
if (cached != null) return cached
val value = withRecursionPrevention(variable) { forVariableTypeNoCache(variable) }
val value = withRecursionPrevention(variable) { forVariableTypeNoCache(variable, checkScope) }
cache[variable] = value
return value
}
private fun forVariableTypeNoCache(variable: PsiVariable): T {
private fun forVariableTypeNoCache(variable: PsiVariable, checkScope: Boolean): T {
if (variable is PsiEnumConstant) return forEnumConstant
val variableType = variable.type
@@ -135,6 +135,10 @@ class TypeConverter(val converter: Converter) {
value = fromAnnotations(variable)
if (value != default) return value
if (checkScope && !converter.inConversionScope(variable)) {
return value
}
if (variable is PsiParameter) {
val scope = variable.declarationScope
if (scope is PsiMethod) {
@@ -143,7 +147,7 @@ class TypeConverter(val converter: Converter) {
val superSignatures = scope.hierarchicalMethodSignature.superSignatures
value = superSignatures.map { signature ->
val params = signature.method.parameterList.parameters
if (paramIndex < params.size) forVariableType(params[paramIndex]) else default
if (paramIndex < params.size) forVariableType(params[paramIndex], false) else default
}.firstOrNull { it != default } ?: default
if (value != default) return value
}
@@ -0,0 +1,45 @@
package test;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class Test {
private String myProp;
private Integer myIntProp;
public void onCreate() {
myProp = "";
myIntProp = 1;
}
public void test() {
foo1(myProp);
foo2(myProp);
foo3(myProp);
myProp.charAt(myIntProp);
System.out.println(myProp);
boolean b = "aaa".equals(myProp);
String s = "aaa" + myProp;
myProp.compareToIgnoreCase(myProp);
List<Integer> list = new ArrayList<Integer>();
list.remove(myIntProp);
}
public void foo1(@NotNull String s) {
}
public void foo2(String s) {
}
public void foo3(@Nullable String s) {
}
}
+42
View File
@@ -0,0 +1,42 @@
package test
import java.util.ArrayList
class Test {
private var myProp: String? = null
private var myIntProp: Int? = null
fun onCreate() {
myProp = ""
myIntProp = 1
}
fun test() {
foo1(myProp!!)
foo2(myProp!!)
foo3(myProp)
myProp!![myIntProp!!]
println(myProp)
val b = "aaa" == myProp
val s = "aaa" + myProp!!
myProp!!.compareTo(myProp!!, ignoreCase = true)
val list = ArrayList<Int>()
list.remove(myIntProp!!)
}
fun foo1(s: String) {
}
fun foo2(s: String) {
}
fun foo3(s: String?) {
}
}
@@ -3493,6 +3493,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("nullableField.java")
public void testNullableField() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/nullableField.java");
doTest(fileName);
}
@TestMetadata("NullableIntNoCrash.java")
public void testNullableIntNoCrash() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/NullableIntNoCrash.java");
@@ -3493,6 +3493,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("nullableField.java")
public void testNullableField() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/nullableField.java");
doTest(fileName);
}
@TestMetadata("NullableIntNoCrash.java")
public void testNullableIntNoCrash() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/nullability/NullableIntNoCrash.java");