diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt index 0455a5d9a7b..37a245cf1e7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt @@ -16,22 +16,19 @@ package org.jetbrains.kotlin.resolve.calls.checkers -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.scopes.JetScope -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE -import org.jetbrains.kotlin.types.expressions.CaptureKind -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.psi.JetFunctionLiteral import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression -import org.jetbrains.kotlin.resolve.calls.callUtil.* -import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.types.expressions.CaptureKind class CapturingInClosureChecker : CallChecker { override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { @@ -45,7 +42,7 @@ class CapturingInClosureChecker : CallChecker { private fun checkCapturingInClosure(variable: VariableDescriptor, trace: BindingTrace, scope: JetScope) { val variableParent = variable.getContainingDeclaration() val scopeContainer = scope.getContainingDeclaration() - if (scopeContainer != variableParent && variableParent is CallableDescriptor) { + if (isCapturedVariable(variableParent, scopeContainer)) { if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) { val inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent) trace.record(CAPTURED_IN_CLOSURE, variable, if (inline) CaptureKind.INLINE_ONLY else CaptureKind.NOT_INLINE) @@ -53,6 +50,18 @@ class CapturingInClosureChecker : CallChecker { } } + private fun isCapturedVariable(variableParent: DeclarationDescriptor, scopeContainer: DeclarationDescriptor): Boolean { + if (variableParent !is FunctionDescriptor || scopeContainer == variableParent) return false + + if (variableParent is ConstructorDescriptor) { + val classDescriptor = variableParent.getContainingDeclaration() + + if (scopeContainer == classDescriptor) return false + if (scopeContainer is PropertyDescriptor && scopeContainer.getContainingDeclaration() == classDescriptor) return false + } + return true + } + private fun isCapturedInInline( context: BindingContext, scopeContainer: DeclarationDescriptor, variableParent: DeclarationDescriptor ): Boolean { @@ -68,7 +77,7 @@ class CapturingInClosureChecker : CallChecker { if (callable is SimpleFunctionDescriptor && callable.getInlineStrategy().isInline()) { val scopeContainerParent = scopeContainer.getContainingDeclaration() assert(scopeContainerParent != null) { "parent is null for " + scopeContainer } - return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent) + return !isCapturedVariable(variableParent, scopeContainerParent) || isCapturedInInline(context, scopeContainerParent, variableParent) } return false } diff --git a/idea/testData/checker/infos/CapturedConstructorParameter.kt b/idea/testData/checker/infos/CapturedConstructorParameter.kt new file mode 100644 index 00000000000..6634dc8c3d3 --- /dev/null +++ b/idea/testData/checker/infos/CapturedConstructorParameter.kt @@ -0,0 +1,52 @@ +trait T +class T1(t: Int): T + +inline fun run(f: () -> T) = f() + + +class Delegate(d: Int) { + fun get(k: Any, m: PropertyMetadata) {} +} + +class A(y: Int, t: Int, d: Int): T by T1(t) { + val a = y + val b by Delegate(d) +} + +class A2(x: Int, y: Int, t: T) { + val t1: T = t + + val x1 = run { x } + init { + run { + y + } + } +} + + +//captured + +class B( + x: Int, + y: Int, + t: Int, + d: Int +) { + init { + class C(a: Int = x): T by T1(t) { + val a = y + val b by Delegate(d) + } + } +} + +class B2(x: Int, y: Int) { + + val x1 = { x }() + init { + { + y + }() + } +} diff --git a/idea/testData/checker/infos/PropertiesWithBackingFields.kt b/idea/testData/checker/infos/PropertiesWithBackingFields.kt index 23e9c579677..ea997261f10 100644 --- a/idea/testData/checker/infos/PropertiesWithBackingFields.kt +++ b/idea/testData/checker/infos/PropertiesWithBackingFields.kt @@ -42,12 +42,12 @@ open class Super(i : Int) -class TestPCParameters(w : Int, x : Int, val y : Int, var z : Int) : Super(w) { +class TestPCParameters(w : Int, x : Int, val y : Int, var z : Int) : Super(w) { - val xx = w + val xx = w init { - w + 1 + w + 1 } fun foo() = x diff --git a/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java index 6f66463545d..5188565f932 100644 --- a/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/checkers/JetPsiCheckerTestGenerated.java @@ -681,6 +681,12 @@ public class JetPsiCheckerTestGenerated extends AbstractJetPsiCheckerTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/checker/infos"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("CapturedConstructorParameter.kt") + public void testCapturedConstructorParameter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/infos/CapturedConstructorParameter.kt"); + doTestWithInfos(fileName); + } + @TestMetadata("CapturedInInlinedClosure.kt") public void testCapturedInInlinedClosure() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/checker/infos/CapturedInInlinedClosure.kt");