Disabled highlighting for variables which are captured in inlined closure.
This commit is contained in:
@@ -372,8 +372,7 @@ public class CodegenBinding {
|
|||||||
public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
|
public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
|
||||||
if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
|
if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
|
||||||
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
|
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
|
||||||
return Boolean.TRUE.equals(bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor)) &&
|
return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
|
||||||
variableDescriptor.isVar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
|
public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ public class JetFlowInformationProvider {
|
|||||||
!DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return;
|
!DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return;
|
||||||
PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
|
PseudocodeVariablesData.VariableUseState variableUseState = in.get(variableDescriptor);
|
||||||
if (instruction instanceof WriteValueInstruction) {
|
if (instruction instanceof WriteValueInstruction) {
|
||||||
if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor)) return;
|
if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null) return;
|
||||||
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
||||||
if (variableUseState != LAST_READ) {
|
if (variableUseState != LAST_READ) {
|
||||||
if (element instanceof JetBinaryExpression &&
|
if (element instanceof JetBinaryExpression &&
|
||||||
|
|||||||
@@ -26,14 +26,13 @@ import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
|
|||||||
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.context.CallCandidateResolutionContext;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
|
|
||||||
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
|
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||||
import org.jetbrains.jet.lang.types.DeferredType;
|
import org.jetbrains.jet.lang.types.DeferredType;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
|
import org.jetbrains.jet.lang.types.expressions.CaptureKind;
|
||||||
import org.jetbrains.jet.util.Box;
|
import org.jetbrains.jet.util.Box;
|
||||||
import org.jetbrains.jet.util.slicedmap.*;
|
import org.jetbrains.jet.util.slicedmap.*;
|
||||||
|
|
||||||
@@ -126,7 +125,7 @@ public interface BindingContext {
|
|||||||
WritableSlice<JetExpression, Boolean> PROCESSED = Slices.createSimpleSetSlice();
|
WritableSlice<JetExpression, Boolean> PROCESSED = Slices.createSimpleSetSlice();
|
||||||
WritableSlice<JetElement, Boolean> STATEMENT = Slices.createRemovableSetSlice();
|
WritableSlice<JetElement, Boolean> STATEMENT = Slices.createRemovableSetSlice();
|
||||||
|
|
||||||
WritableSlice<VariableDescriptor, Boolean> CAPTURED_IN_CLOSURE = Slices.createSimpleSetSlice();
|
WritableSlice<VariableDescriptor, CaptureKind> CAPTURED_IN_CLOSURE = new BasicWritableSlice<VariableDescriptor, CaptureKind>(DO_NOTHING);
|
||||||
|
|
||||||
// enum DeferredTypeKey {DEFERRED_TYPE_KEY}
|
// enum DeferredTypeKey {DEFERRED_TYPE_KEY}
|
||||||
// WritableSlice<DeferredTypeKey, Collection<DeferredType>> DEFERRED_TYPES = Slices.createSimpleSlice();
|
// WritableSlice<DeferredTypeKey, Collection<DeferredType>> DEFERRED_TYPES = Slices.createSimpleSlice();
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2013 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.lang.types.expressions;
|
||||||
|
|
||||||
|
public enum CaptureKind {
|
||||||
|
NOT_INLINE,
|
||||||
|
INLINE_ONLY
|
||||||
|
}
|
||||||
+64
-6
@@ -33,14 +33,15 @@ import org.jetbrains.jet.lang.diagnostics.Diagnostic;
|
|||||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.*;
|
import org.jetbrains.jet.lang.resolve.*;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||||
@@ -139,12 +140,69 @@ public class ExpressionTypingUtils {
|
|||||||
).contains(expression.getNode().getElementType());
|
).contains(expression.getNode().getElementType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isCapturedInInline(
|
||||||
|
@NotNull BindingContext context,
|
||||||
|
@NotNull DeclarationDescriptor scopeContainer,
|
||||||
|
@NotNull DeclarationDescriptor variableParent
|
||||||
|
) {
|
||||||
|
PsiElement scopeDeclaration = BindingContextUtils.descriptorToDeclaration(context, scopeContainer);
|
||||||
|
if (!(scopeDeclaration instanceof JetFunctionLiteral)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PsiElement parent = scopeDeclaration.getParent();
|
||||||
|
assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent;
|
||||||
|
JetCallExpression callExpression = getCallExpression((JetFunctionLiteralExpression) parent);
|
||||||
|
if (callExpression == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolvedCall<? extends CallableDescriptor> call = context.get(BindingContext.RESOLVED_CALL, callExpression.getCalleeExpression());
|
||||||
|
if (call == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallableDescriptor callable = call.getResultingDescriptor();
|
||||||
|
if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).isInline()) {
|
||||||
|
DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration();
|
||||||
|
assert scopeContainerParent != null : "parent is null for " + scopeContainer;
|
||||||
|
return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static JetCallExpression getCallExpression(@NotNull JetFunctionLiteralExpression functionLiteralExpression) {
|
||||||
|
PsiElement parent = functionLiteralExpression.getParent();
|
||||||
|
if (parent instanceof JetValueArgument) {
|
||||||
|
// foo({ ... }) or foo(f = { ... })
|
||||||
|
|
||||||
|
PsiElement valueArgumentList = parent.getParent();
|
||||||
|
assert valueArgumentList instanceof JetValueArgumentList : "parent of value argument is " + valueArgumentList;
|
||||||
|
|
||||||
|
if (valueArgumentList.getParent() instanceof JetCallExpression) { // may be argument list of annotation
|
||||||
|
return (JetCallExpression) valueArgumentList.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parent instanceof JetCallExpression) {
|
||||||
|
// foo { ... }
|
||||||
|
|
||||||
|
return (JetCallExpression) parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
|
public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
|
||||||
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
|
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
|
||||||
if (variable != null) {
|
if (variable != null) {
|
||||||
DeclarationDescriptor containingDeclaration = variable.getContainingDeclaration();
|
DeclarationDescriptor variableParent = variable.getContainingDeclaration();
|
||||||
if (scope.getContainingDeclaration() != containingDeclaration && containingDeclaration instanceof CallableDescriptor) {
|
DeclarationDescriptor scopeContainer = scope.getContainingDeclaration();
|
||||||
trace.record(CAPTURED_IN_CLOSURE, variable);
|
if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) {
|
||||||
|
if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
|
||||||
|
boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent);
|
||||||
|
trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor;
|
|||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
|
import org.jetbrains.jet.lang.types.expressions.CaptureKind;
|
||||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
||||||
@@ -88,7 +89,7 @@ class VariablesHighlightingVisitor extends AfterAnalysisHighlightingVisitor {
|
|||||||
JetPsiChecker.highlightName(holder, elementToHighlight, JetHighlightingColors.MUTABLE_VARIABLE);
|
JetPsiChecker.highlightName(holder, elementToHighlight, JetHighlightingColors.MUTABLE_VARIABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Boolean.TRUE.equals(bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor))) {
|
if (bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) == CaptureKind.NOT_INLINE) {
|
||||||
String msg = ((VariableDescriptor) descriptor).isVar()
|
String msg = ((VariableDescriptor) descriptor).isVar()
|
||||||
? "Wrapped into a reference object to be modified when captured in a closure"
|
? "Wrapped into a reference object to be modified when captured in a closure"
|
||||||
: "Value captured in a closure";
|
: "Value captured in a closure";
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<info>inline</info> fun <T> run(f: () -> T) = f()
|
||||||
|
fun run2(f: () -> Unit) = f()
|
||||||
|
|
||||||
|
fun inline() {
|
||||||
|
val x = 1
|
||||||
|
run { x }
|
||||||
|
|
||||||
|
val x1 = 1
|
||||||
|
run ({ x1 })
|
||||||
|
|
||||||
|
val x2 = 1
|
||||||
|
run (f = { x2 })
|
||||||
|
|
||||||
|
val x3 = 1
|
||||||
|
run {
|
||||||
|
run {
|
||||||
|
x3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notInline() {
|
||||||
|
val <info descr="Value captured in a closure">y2</info> = 1
|
||||||
|
run { <info descr="Value captured in a closure">y2</info> }
|
||||||
|
run2 { <info descr="Value captured in a closure">y2</info> }
|
||||||
|
|
||||||
|
val <info descr="Value captured in a closure">y3</info> = 1
|
||||||
|
run2 { <info descr="Value captured in a closure">y3</info> }
|
||||||
|
run { <info descr="Value captured in a closure">y3</info> }
|
||||||
|
|
||||||
|
// wrapped, using in not inline
|
||||||
|
val <info descr="Value captured in a closure">z</info> = 2
|
||||||
|
{ <info descr="Value captured in a closure">z</info> }()
|
||||||
|
|
||||||
|
val <info descr="Value captured in a closure">z1</info> = 3
|
||||||
|
run2 { <info descr="Value captured in a closure">z1</info> }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nestedDifferent() { // inline within non-inline and vice-versa
|
||||||
|
val <info descr="Value captured in a closure">y</info> = 1
|
||||||
|
{
|
||||||
|
run {
|
||||||
|
<info descr="Value captured in a closure">y</info>
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
val <info descr="Value captured in a closure">y1</info> = 1
|
||||||
|
run {
|
||||||
|
{ <info descr="Value captured in a closure">y1</info> }()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun localFunctionAndClass() {
|
||||||
|
val <info descr="Value captured in a closure">u</info> = 1
|
||||||
|
fun localFun() {
|
||||||
|
run {
|
||||||
|
<info descr="Value captured in a closure">u</info>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val <info descr="Value captured in a closure"><warning>v</warning></info> = 1 // erroneous "unused warning" is caused by KT-3501
|
||||||
|
class LocalClass {
|
||||||
|
fun f() {
|
||||||
|
run {
|
||||||
|
<info descr="Value captured in a closure">v</info>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun objectExpression() {
|
||||||
|
val <info descr="Value captured in a closure">u1</info> = 1
|
||||||
|
object : Any() {
|
||||||
|
fun f() {
|
||||||
|
run {
|
||||||
|
<info descr="Value captured in a closure">u1</info>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val <info descr="Value captured in a closure">u2</info> = 1
|
||||||
|
object : Any() {
|
||||||
|
val <info>prop</info> = run {
|
||||||
|
<info descr="Value captured in a closure">u2</info>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val <info descr="Value captured in a closure">u3</info> = ""
|
||||||
|
object : Throwable(run { <info descr="Value captured in a closure">u3</info> }) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -417,6 +417,11 @@ public class JetPsiCheckerTestGenerated extends AbstractJetPsiCheckerTest {
|
|||||||
doTestWithInfos("idea/testData/checker/infos/Autocasts.kt");
|
doTestWithInfos("idea/testData/checker/infos/Autocasts.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("CapturedInInlinedClosure.kt")
|
||||||
|
public void testCapturedInInlinedClosure() throws Exception {
|
||||||
|
doTestWithInfos("idea/testData/checker/infos/CapturedInInlinedClosure.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("PropertiesWithBackingFields.kt")
|
@TestMetadata("PropertiesWithBackingFields.kt")
|
||||||
public void testPropertiesWithBackingFields() throws Exception {
|
public void testPropertiesWithBackingFields() throws Exception {
|
||||||
doTestWithInfos("idea/testData/checker/infos/PropertiesWithBackingFields.kt");
|
doTestWithInfos("idea/testData/checker/infos/PropertiesWithBackingFields.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user