Check whether the field is indeed being initialized

in checkFieldInExactlyOnceLambdaInitialization
 #KT-40691 Fixed
This commit is contained in:
Ilmir Usmanov
2020-07-30 14:13:51 +02:00
parent d8b76f5b26
commit 2c205410fa
12 changed files with 242 additions and 2 deletions
@@ -5131,6 +5131,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Errors.CAPTURED_VAL_INITIALIZATION
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -54,9 +55,10 @@ class CapturingInClosureChecker : CallChecker {
variable: VariableDescriptor,
trace: BindingTrace,
scopeContainer: DeclarationDescriptor,
reportOn: PsiElement
nameElement: PsiElement
) {
if (variable !is PropertyDescriptor || scopeContainer !is AnonymousFunctionDescriptor || variable.isVar) return
if ((nameElement as KtExpression).getAssignmentByLHS() == null) return
val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer) as? KtFunction ?: return
if (scopeContainer.containingDeclaration !is ConstructorDescriptor) return
if (!isExactlyOnceContract(trace.bindingContext, scopeDeclaration)) return
@@ -64,7 +66,7 @@ class CapturingInClosureChecker : CallChecker {
val (callee, param) = getCalleeDescriptorAndParameter(trace.bindingContext, scopeDeclaration) ?: return
if (callee !is FunctionDescriptor) return
if (!callee.isInline || (param.isCrossinline || !InlineUtil.isInlineParameter(param))) {
trace.report(CAPTURED_VAL_INITIALIZATION.on(reportOn as KtExpression, variable))
trace.report(CAPTURED_VAL_INITIALIZATION.on(nameElement, variable))
}
}
@@ -0,0 +1,28 @@
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
// IGNORE_BACKEND: NATIVE
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
import kotlin.contracts.*
class A {
val value = "Some value"
init {
foo {
println(value)
}
}
}
fun foo(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
}
fun box(): String {
A()
return "OK"
}
@@ -58,3 +58,74 @@ class Test {
}
}
}
@kotlin.contracts.ExperimentalContracts
class Test1 {
val a: String = ""
val b: String = ""
val c: String = ""
val d: String = ""
init {
inlineMe {
a += "allowed"
}
crossinlineMe {
b += "not allowed"
}
noinlineMe {
c += "not allowed"
}
notinline {
d += "not allowed"
}
}
}
@kotlin.contracts.ExperimentalContracts
class Test2 {
val a: String = ""
val b: String = ""
val c: String = ""
val d: String = ""
init {
var blackhole = ""
inlineMe {
blackhole += a
}
crossinlineMe {
blackhole += b
}
noinlineMe {
blackhole += c
}
notinline {
blackhole += d
}
}
}
@kotlin.contracts.ExperimentalContracts
class Test4 {
val a: String = ""
val b: String = ""
val c: String = ""
val d: String = ""
init {
var blackhole: String
inlineMe {
blackhole = a
}
crossinlineMe {
blackhole = b
}
noinlineMe {
blackhole = c
}
notinline {
blackhole = d
}
}
}
@@ -58,3 +58,74 @@ class Test {
}
}
}
@kotlin.contracts.ExperimentalContracts
class Test1 {
val a: String = ""
val b: String = ""
val c: String = ""
val d: String = ""
init {
inlineMe {
<!VAL_REASSIGNMENT!>a<!> += "allowed"
}
crossinlineMe {
<!CAPTURED_VAL_INITIALIZATION, VAL_REASSIGNMENT!>b<!> += "not allowed"
}
noinlineMe {
<!CAPTURED_VAL_INITIALIZATION, VAL_REASSIGNMENT!>c<!> += "not allowed"
}
notinline {
<!CAPTURED_VAL_INITIALIZATION, VAL_REASSIGNMENT!>d<!> += "not allowed"
}
}
}
@kotlin.contracts.ExperimentalContracts
class Test2 {
val a: String = ""
val b: String = ""
val c: String = ""
val d: String = ""
init {
var blackhole = ""
inlineMe {
blackhole += a
}
crossinlineMe {
blackhole += b
}
noinlineMe {
blackhole += c
}
notinline {
blackhole += d
}
}
}
@kotlin.contracts.ExperimentalContracts
class Test4 {
val a: String = ""
val b: String = ""
val c: String = ""
val d: String = ""
init {
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>blackhole<!>: String
inlineMe {
blackhole = a
}
crossinlineMe {
blackhole = b
}
noinlineMe {
blackhole = c
}
notinline {
blackhole = d
}
}
}
@@ -22,3 +22,36 @@ package
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.contracts.ExperimentalContracts public final class Test1 {
public constructor Test1()
public final val a: kotlin.String = ""
public final val b: kotlin.String = ""
public final val c: kotlin.String = ""
public final val d: kotlin.String = ""
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.contracts.ExperimentalContracts public final class Test2 {
public constructor Test2()
public final val a: kotlin.String = ""
public final val b: kotlin.String = ""
public final val c: kotlin.String = ""
public final val d: kotlin.String = ""
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.contracts.ExperimentalContracts public final class Test4 {
public constructor Test4()
public final val a: kotlin.String = ""
public final val b: kotlin.String = ""
public final val c: kotlin.String = ""
public final val d: kotlin.String = ""
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -5161,6 +5161,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");
@@ -5161,6 +5161,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");
@@ -5131,6 +5131,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");
@@ -4166,6 +4166,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");
@@ -4166,6 +4166,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");
@@ -4166,6 +4166,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/contracts/exception.kt");
}
@TestMetadata("fieldReadInConstructor.kt")
public void testFieldReadInConstructor() throws Exception {
runTest("compiler/testData/codegen/box/contracts/fieldReadInConstructor.kt");
}
@TestMetadata("forLoop.kt")
public void testForLoop() throws Exception {
runTest("compiler/testData/codegen/box/contracts/forLoop.kt");