Do not lose information about contracts if call uses default value
It is safe to treat DefaultValueArgument as UNKNOWN_COMPUTATION, because default arguments can't break smartcasts. Possibly, they can add new ones, but it can be supported later. ^KT-25278 Fixed
This commit is contained in:
@@ -34,8 +34,7 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
@@ -170,14 +169,36 @@ class EffectsExtractingVisitor(
|
|||||||
arguments.addIfNotNull(extensionReceiver?.toComputation())
|
arguments.addIfNotNull(extensionReceiver?.toComputation())
|
||||||
arguments.addIfNotNull(dispatchReceiver?.toComputation())
|
arguments.addIfNotNull(dispatchReceiver?.toComputation())
|
||||||
|
|
||||||
valueArgumentsByIndex?.mapTo(arguments) {
|
val passedValueArguments = valueArgumentsByIndex ?: return null
|
||||||
val valueArgument = (it as? ExpressionValueArgument)?.valueArgument ?: return null
|
|
||||||
when (valueArgument) {
|
passedValueArguments.mapTo(arguments) { it.toComputation() ?: return null }
|
||||||
is KtLambdaArgument -> valueArgument.getLambdaExpression()?.let { ESLambda(it) } ?: return null
|
|
||||||
else -> extractOrGetCached(valueArgument.getArgumentExpression() ?: return null)
|
|
||||||
}
|
|
||||||
} ?: return null
|
|
||||||
|
|
||||||
return arguments
|
return arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ResolvedValueArgument.toComputation(): Computation? {
|
||||||
|
return when (this) {
|
||||||
|
// Assume that we don't know anything about default arguments
|
||||||
|
// Note that we don't want to return 'null' here, because 'null' indicates that we can't
|
||||||
|
// analyze whole call, which is undesired for cases like `kotlin.test.assertNotNull`
|
||||||
|
is DefaultValueArgument -> UNKNOWN_COMPUTATION
|
||||||
|
|
||||||
|
// We prefer to throw away calls with varags completely, just to be safe
|
||||||
|
// Potentially, we could return UNKNOWN_COMPUTATION here too
|
||||||
|
is VarargValueArgument -> null
|
||||||
|
|
||||||
|
is ExpressionValueArgument -> valueArgument?.toComputation()
|
||||||
|
|
||||||
|
// Should be exhaustive
|
||||||
|
else -> throw IllegalStateException("Unexpected ResolvedValueArgument $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ValueArgument.toComputation(): Computation? {
|
||||||
|
return when (this) {
|
||||||
|
is KtLambdaArgument -> getLambdaExpression()?.let { ESLambda(it) }
|
||||||
|
is KtValueArgument -> getArgumentExpression()?.let { extractOrGetCached(it) }
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// !LANGUAGE: +AllowContractsForCustomFunctions +UseReturnsEffect
|
||||||
|
// !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER
|
||||||
|
|
||||||
|
import kotlin.internal.contracts.*
|
||||||
|
|
||||||
|
fun myAssert(condition: Boolean, message: String = "") {
|
||||||
|
contract {
|
||||||
|
returns() implies (condition)
|
||||||
|
}
|
||||||
|
if (!condition) throw kotlin.IllegalArgumentException(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(x: Any?) {
|
||||||
|
myAssert(x is String)
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.length
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun myAssert(/*0*/ condition: kotlin.Boolean, /*1*/ message: kotlin.String = ...): kotlin.Unit
|
||||||
|
Returns(WILDCARD) -> condition
|
||||||
|
|
||||||
|
public fun test(/*0*/ x: kotlin.Any?): kotlin.Unit
|
||||||
+5
@@ -1211,6 +1211,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callWithDefaultValue.kt")
|
||||||
|
public void testCallWithDefaultValue() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/callWithDefaultValue.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("catchExceptionSpilling.kt")
|
@TestMetadata("catchExceptionSpilling.kt")
|
||||||
public void testCatchExceptionSpilling() throws Exception {
|
public void testCatchExceptionSpilling() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/catchExceptionSpilling.kt");
|
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/catchExceptionSpilling.kt");
|
||||||
|
|||||||
compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
Generated
+5
@@ -1211,6 +1211,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callWithDefaultValue.kt")
|
||||||
|
public void testCallWithDefaultValue() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/callWithDefaultValue.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("catchExceptionSpilling.kt")
|
@TestMetadata("catchExceptionSpilling.kt")
|
||||||
public void testCatchExceptionSpilling() throws Exception {
|
public void testCatchExceptionSpilling() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/catchExceptionSpilling.kt");
|
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/catchExceptionSpilling.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user