From 4b35e3b1358dda468ec3eb8538ed4655302b7063 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 28 Sep 2015 17:18:19 +0300 Subject: [PATCH] Preliminary declaration visitor for estimating local variable's predictability for smart casts Predictability estimation algorithm is completely new, but backward compatibility should present. A large set of tests. Some updated tests. Smart casts allowed for captured variables if they are not modified in closure #KT-9051 Fixed Also #KT-8643 Fixed Also #KT-7976 Fixed Correct handling of lambda arguments in functions #KT-9143 Fixed --- .../jetbrains/kotlin/psi/psiUtil/psiUtils.kt | 2 + .../kotlin/resolve/BindingContext.java | 2 + .../kotlin/resolve/BodyResolver.java | 5 + .../kotlin/resolve/DescriptorResolver.java | 2 + .../resolve/FunctionDescriptorResolver.kt | 2 + .../kotlin/resolve/ScriptBodyResolver.java | 2 + .../smartcasts/DataFlowValueFactory.java | 136 +++++++++++++---- .../expressions/AssignedVariablesSearcher.kt | 41 ++++- .../ExpressionTypingVisitorForStatements.java | 4 +- .../PreliminaryDeclarationVisitor.kt | 65 ++++++++ .../expressions/PreliminaryLoopVisitor.kt | 2 +- .../diagnostics/tests/infos/SmartCasts.kt | 6 +- .../diagnostics/tests/script/varInScript.kt | 6 + .../diagnostics/tests/script/varInScript.txt | 10 ++ .../tests/smartCasts/lambdaAndArgument.kt | 14 ++ .../tests/smartCasts/lambdaAndArgument.txt | 5 + .../tests/smartCasts/lambdaAndArgumentFun.kt | 14 ++ .../tests/smartCasts/lambdaAndArgumentFun.txt | 5 + .../smartCasts/lambdaDeclaresAndModifies.kt | 10 ++ .../smartCasts/lambdaDeclaresAndModifies.txt | 4 + .../lambdaDeclaresAndModifiesInLoop.kt | 12 ++ .../lambdaDeclaresAndModifiesInLoop.txt | 4 + .../lambdaDeclaresAndModifiesInSecondary.kt | 12 ++ .../lambdaDeclaresAndModifiesInSecondary.txt | 10 ++ .../lambdaDeclaresAndModifiesWithDirectEq.kt | 22 +++ .../lambdaDeclaresAndModifiesWithDirectEq.txt | 13 ++ .../smartCasts/lambdaUsesOwnerModifies.kt | 12 ++ .../smartCasts/lambdaUsesOwnerModifies.txt | 4 + .../tests/smartCasts/localClassChanges.kt | 15 ++ .../tests/smartCasts/localClassChanges.txt | 4 + .../tests/smartCasts/localFunBetween.kt | 15 ++ .../tests/smartCasts/localFunBetween.txt | 19 +++ .../tests/smartCasts/localFunChanges.kt | 14 ++ .../tests/smartCasts/localFunChanges.txt | 4 + .../tests/smartCasts/localObjectChanges.kt | 15 ++ .../tests/smartCasts/localObjectChanges.txt | 4 + .../smartCasts/ownerDeclaresBothModifies.kt | 15 ++ .../smartCasts/ownerDeclaresBothModifies.txt | 4 + .../smartCasts/varChangedInInitializer.kt | 7 + .../smartCasts/varChangedInInitializer.txt | 8 + .../varChangedInLocalInitializer.kt | 10 ++ .../varChangedInLocalInitializer.txt | 3 + .../tests/smartCasts/varInAccessor.kt | 17 +++ .../tests/smartCasts/varInAccessor.txt | 3 + .../tests/smartCasts/varInInitializer.kt | 12 ++ .../tests/smartCasts/varInInitializer.txt | 9 ++ .../smartCasts/varInSecondaryConstructor.kt | 10 ++ .../smartCasts/varInSecondaryConstructor.txt | 11 ++ .../tests/smartCasts/varInsideLocalFun.kt | 17 +++ .../tests/smartCasts/varInsideLocalFun.txt | 11 ++ .../variables/accessorAndFunction.kt | 23 +++ .../variables/accessorAndFunction.txt | 10 ++ .../smartCasts/variables/inPropertySam.kt | 18 +++ .../smartCasts/variables/inPropertySam.txt | 18 +++ .../variables/lambdaBetweenArguments.kt | 9 ++ .../variables/lambdaBetweenArguments.txt | 4 + .../varnotnull/varCapturedInSafeClosure.kt | 4 +- .../testsWithStdLib/smartcasts/forEachSafe.kt | 11 ++ .../smartcasts/forEachSafe.txt | 3 + .../smartcasts/forEachUnsafe.kt | 14 ++ .../smartcasts/forEachUnsafe.txt | 3 + .../smartcasts/lazyDeclaresAndModifies.kt | 11 ++ .../smartcasts/lazyDeclaresAndModifies.txt | 10 ++ .../smartcasts/letAlwaysChangesToNotNull.kt | 7 + .../smartcasts/letAlwaysChangesToNotNull.txt | 3 + .../smartcasts/letChangesToNotNull.kt | 7 + .../smartcasts/letChangesToNotNull.txt | 3 + .../smartcasts/letChangesToNull.kt | 9 ++ .../smartcasts/letChangesToNull.txt | 3 + .../smartcasts/letChangesToNullComplex.kt | 11 ++ .../smartcasts/letChangesToNullComplex.txt | 4 + .../smartcasts/letMergeNotNull.kt | 9 ++ .../smartcasts/letMergeNotNull.txt | 3 + .../testsWithStdLib/smartcasts/letStable.kt | 10 ++ .../testsWithStdLib/smartcasts/letStable.txt | 3 + .../smartcasts/withChangesToNull.kt | 10 ++ .../smartcasts/withChangesToNull.txt | 3 + .../checkers/JetDiagnosticsTestGenerated.java | 141 ++++++++++++++++++ ...JetDiagnosticsTestWithStdLibGenerated.java | 60 ++++++++ .../kotlin/idea/analysis/AnalyzerUtil.kt | 2 + .../caches/resolve/CodeFragmentAnalyzer.kt | 2 + .../branchedTransformations/IfThenUtils.kt | 2 +- .../ReplaceWithAnnotationAnalyzer.kt | 2 + idea/testData/checker/infos/SmartCasts.kt | 5 +- 84 files changed, 1047 insertions(+), 43 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryDeclarationVisitor.kt create mode 100644 compiler/testData/diagnostics/tests/script/varInScript.kt create mode 100644 compiler/testData/diagnostics/tests/script/varInScript.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localClassChanges.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localFunBetween.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localFunBetween.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localFunChanges.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localFunChanges.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInAccessor.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInAccessor.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInInitializer.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInInitializer.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index c3183d6e7da..1be46e56ff0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -310,3 +310,5 @@ public fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUt public fun E.createSmartPointer(): SmartPsiElementPointer = SmartPointerManager.getInstance(getProject()).createSmartPsiElementPointer(this) + +public fun PsiElement.before(element: PsiElement) = textRange.endOffset <= element.textRange.startOffset diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index bdb28503ccb..7ccbcdd5fb9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.types.DeferredType; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.expressions.CaptureKind; import org.jetbrains.kotlin.types.expressions.JetTypeInfo; +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; import org.jetbrains.kotlin.util.Box; import org.jetbrains.kotlin.util.slicedMap.*; @@ -150,6 +151,7 @@ public interface BindingContext { WritableSlice UNREACHABLE_CODE = Slices.createSimpleSetSlice(); WritableSlice CAPTURED_IN_CLOSURE = new BasicWritableSlice(DO_NOTHING); + WritableSlice PRELIMINARY_VISITOR = new BasicWritableSlice(DO_NOTHING); WritableSlice NEED_SYNTHETIC_ACCESSOR = new BasicWritableSlice(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index f66072c8567..f2f07bab29f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; import org.jetbrains.kotlin.types.expressions.ValueParameterResolver; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt; import org.jetbrains.kotlin.util.Box; @@ -521,6 +522,8 @@ public class BodyResolver { if (!classDescriptor.getConstructors().isEmpty()) { JetExpression body = anonymousInitializer.getBody(); if (body != null) { + PreliminaryDeclarationVisitor.Companion.createForDeclaration( + (JetDeclaration) anonymousInitializer.getParent().getParent(), trace); expressionTypingServices.getType(scopeForInitializers, body, NO_EXPECTED_TYPE, outerDataFlowInfo, trace); } processModifiersOnInitializer(anonymousInitializer, scopeForInitializers); @@ -582,6 +585,7 @@ public class BodyResolver { ) { computeDeferredType(propertyDescriptor.getReturnType()); + PreliminaryDeclarationVisitor.Companion.createForDeclaration(property, trace); JetExpression initializer = property.getInitializer(); LexicalScope propertyScope = getScopeForProperty(c, property); if (parentScope == null) { @@ -784,6 +788,7 @@ public class BodyResolver { @Nullable Function1 beforeBlockBody, @NotNull CallChecker callChecker ) { + PreliminaryDeclarationVisitor.Companion.createForDeclaration(function, trace); LexicalScope innerScope = FunctionDescriptorUtil.getFunctionInnerScope(scope, functionDescriptor, trace); List valueParameters = function.getValueParameters(); List valueParameterDescriptors = functionDescriptor.getValueParameters(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index 471ec02244a..f210a5ad6b7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -55,6 +55,7 @@ import org.jetbrains.kotlin.storage.StorageManager; import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.checker.JetTypeChecker; import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; import java.util.*; @@ -865,6 +866,7 @@ public class DescriptorResolver { new Function0() { @Override public JetType invoke() { + PreliminaryDeclarationVisitor.Companion.createForDeclaration(variable, trace); JetType initializerType = resolveInitializerType(scope, variable.getInitializer(), dataFlowInfo, trace); setConstantForVariableIfNeeded(variableDescriptor, scope, variable, dataFlowInfo, initializerType, trace); return transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index 0ba6463d464..10b3102f929 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.checker.JetTypeChecker import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor import java.util.* class FunctionDescriptorResolver( @@ -123,6 +124,7 @@ class FunctionDescriptorResolver( } else if (function.hasBody()) { DeferredType.createRecursionIntolerant(storageManager, trace) { + PreliminaryDeclarationVisitor.createForDeclaration(function, trace); val type = expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor) transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ScriptBodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ScriptBodyResolver.java index 7919ee58535..db6bba7772d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ScriptBodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ScriptBodyResolver.java @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.expressions.CoercionStrategy; import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; import java.util.Map; @@ -63,6 +64,7 @@ public class ScriptBodyResolver { DataFlowInfo.EMPTY, NO_EXPECTED_TYPE ); + PreliminaryDeclarationVisitor.Companion.createForDeclaration(script, trace); JetType returnType = expressionTypingServices.getBlockReturnedType(script.getBlockExpression(), CoercionStrategy.NO_COERCION, context).getType(); if (returnType == null) { returnType = ErrorUtils.createErrorType("getBlockReturnedType returned null"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java index d93f1ece959..a73590e95ce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java @@ -17,28 +17,36 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts; import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.JetNodeTypes; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; +import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor; import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind; import org.jetbrains.kotlin.resolve.scopes.receivers.*; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils; +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor; + +import java.util.Set; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing; +import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR; import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET; +import static org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.*; /** * This class is intended to create data flow values for different kind of expressions. @@ -84,7 +92,7 @@ public class DataFlowValueFactory { // fun foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable return new DataFlowValue(expression, type, - DataFlowValue.Kind.OTHER, + OTHER, Nullability.NOT_NULL); } @@ -98,7 +106,7 @@ public class DataFlowValueFactory { @NotNull public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) { JetType type = receiver.getType(); - return new DataFlowValue(receiver, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type)); + return new DataFlowValue(receiver, type, STABLE_VALUE, getImmanentNullability(type)); } @NotNull @@ -119,7 +127,7 @@ public class DataFlowValueFactory { if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) { // SCRIPT: smartcasts data flow JetType type = receiverValue.getType(); - return new DataFlowValue(receiverValue, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type)); + return new DataFlowValue(receiverValue, type, STABLE_VALUE, getImmanentNullability(type)); } else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) { return createDataFlowValue((ThisReceiver) receiverValue); @@ -139,14 +147,16 @@ public class DataFlowValueFactory { } @NotNull - public static DataFlowValue createDataFlowValue( + public static DataFlowValue createDataFlowValueForProperty( + @NotNull JetProperty property, @NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext, @Nullable ModuleDescriptor usageContainingModule ) { JetType type = variableDescriptor.getType(); return new DataFlowValue(variableDescriptor, type, - variableKind(variableDescriptor, usageContainingModule, bindingContext), + variableKind(variableDescriptor, usageContainingModule, + bindingContext, property), getImmanentNullability(type)); } @@ -157,17 +167,17 @@ public class DataFlowValueFactory { private static class IdentifierInfo { public final Object id; - public final DataFlowValue.Kind kind; + public final Kind kind; public final boolean isPackage; - private IdentifierInfo(Object id, DataFlowValue.Kind kind, boolean isPackage) { + private IdentifierInfo(Object id, Kind kind, boolean isPackage) { this.id = id; this.kind = kind; this.isPackage = isPackage; } } - private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, DataFlowValue.Kind.OTHER, false) { + private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, OTHER, false) { @Override public String toString() { return "NO_IDENTIFIER_INFO"; @@ -175,18 +185,18 @@ public class DataFlowValueFactory { }; @NotNull - private static IdentifierInfo createInfo(Object id, DataFlowValue.Kind kind) { + private static IdentifierInfo createInfo(Object id, Kind kind) { return new IdentifierInfo(id, kind, false); } @NotNull private static IdentifierInfo createStableInfo(Object id) { - return createInfo(id, DataFlowValue.Kind.STABLE_VALUE); + return createInfo(id, STABLE_VALUE); } @NotNull private static IdentifierInfo createPackageOrClassInfo(Object id) { - return new IdentifierInfo(id, DataFlowValue.Kind.STABLE_VALUE, true); + return new IdentifierInfo(id, STABLE_VALUE, true); } @NotNull @@ -199,9 +209,9 @@ public class DataFlowValueFactory { } return createInfo(Pair.create(receiverInfo.id, selectorInfo.id), receiverInfo.kind.isStable() && selectorInfo.kind.isStable() - ? DataFlowValue.Kind.STABLE_VALUE + ? STABLE_VALUE // x.y can never be a local variable - : DataFlowValue.Kind.OTHER); + : OTHER); } @NotNull @@ -275,8 +285,10 @@ public class DataFlowValueFactory { resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null; VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor; - return combineInfo(receiverInfo, createInfo(variableDescriptor, - variableKind(variableDescriptor, usageModuleDescriptor, bindingContext))); + return combineInfo(receiverInfo, + createInfo(variableDescriptor, + variableKind(variableDescriptor, usageModuleDescriptor, + bindingContext, simpleNameExpression))); } if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) { return createPackageOrClassInfo(declarationDescriptor); @@ -311,18 +323,92 @@ public class DataFlowValueFactory { return NO_IDENTIFIER_INFO; } - public static DataFlowValue.Kind variableKind( + @NotNull + private static DeclarationDescriptor getVariableContainingDeclaration(@NotNull VariableDescriptor variableDescriptor) { + DeclarationDescriptor containingDeclarationDescriptor = variableDescriptor.getContainingDeclaration(); + if (containingDeclarationDescriptor instanceof ConstructorDescriptor + && ((ConstructorDescriptor) containingDeclarationDescriptor).isPrimary()) { + // This code is necessary just because JetClassInitializer has no associated descriptor in trace + // Because of it we have to use class itself instead of initializer, + // otherwise we could not find this descriptor inside isAccessedInsideClosure below + containingDeclarationDescriptor = containingDeclarationDescriptor.getContainingDeclaration(); + assert containingDeclarationDescriptor != null : "No containing declaration for primary constructor"; + } + return containingDeclarationDescriptor; + } + + private static boolean isAccessedInsideClosure( + @NotNull DeclarationDescriptor variableContainingDeclaration, + @NotNull BindingContext bindingContext, + @NotNull JetElement accessElement + ) { + PsiElement parent = accessElement.getParent(); + while (parent != null) { + // We are inside some declaration + if (parent instanceof JetDeclarationWithBody || parent instanceof JetClassOrObject) { + DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent); + if (variableContainingDeclaration.equals(descriptor)) { + // Access is at the same declaration: not in closure + break; + } + else { + // Access is lower than parent: in closure + return true; + } + } + parent = parent.getParent(); + } + return false; + } + + private static boolean isAccessedBeforeAllClosureWriters( + @NotNull DeclarationDescriptor variableContainingDeclaration, + @NotNull Set writers, + @NotNull BindingContext bindingContext, + @NotNull JetElement accessElement + ) { + // All writers should be before access element, with the exception: + // writer which is the same with declaration site does not count + for (JetDeclaration writer : writers) { + DeclarationDescriptor writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer); + // Access is after some writer + if (!variableContainingDeclaration.equals(writerDescriptor) && !PsiUtilsKt.before(accessElement, writer)) { + return false; + } + } + // Access is before all writers + return true; + } + + private static Kind variableKind( @NotNull VariableDescriptor variableDescriptor, @Nullable ModuleDescriptor usageModule, - @NotNull BindingContext bindingContext + @NotNull BindingContext bindingContext, + @NotNull JetElement accessElement ) { - if (isStableVariable(variableDescriptor, usageModule)) return DataFlowValue.Kind.STABLE_VALUE; + if (isStableValue(variableDescriptor, usageModule)) return STABLE_VALUE; boolean isLocalVar = variableDescriptor.isVar() && variableDescriptor instanceof LocalVariableDescriptor; - if (!isLocalVar) return DataFlowValue.Kind.OTHER; - if (BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor)) { - return DataFlowValue.Kind.UNPREDICTABLE_VARIABLE; - } - return DataFlowValue.Kind.PREDICTABLE_VARIABLE; + if (!isLocalVar) return OTHER; + if (variableDescriptor instanceof SyntheticFieldDescriptor) return OTHER; + + // Local variable classification: PREDICTABLE or UNPREDICTABLE + PreliminaryDeclarationVisitor preliminaryVisitor = + PreliminaryDeclarationVisitor.Companion.getVisitorByVariable(variableDescriptor, bindingContext); + // A case when we just analyse an expression alone: counts as unpredictable + if (preliminaryVisitor == null) return UNPREDICTABLE_VARIABLE; + + // Analyze who writes variable + // If there is no writer: predictable + Set writers = preliminaryVisitor.writers(variableDescriptor); + if (writers.isEmpty()) return PREDICTABLE_VARIABLE; + + // If access element is inside closure: unpredictable + DeclarationDescriptor variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor); + if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE; + + // Otherwise, predictable iff considered position is BEFORE all writers except declarer itself + if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement)) return PREDICTABLE_VARIABLE; + else return UNPREDICTABLE_VARIABLE; } /** @@ -339,7 +425,7 @@ public class DataFlowValueFactory { * @param usageModule a module with a considered usage place, or null if it's not known (not recommended) * @return true if variable is stable, false otherwise */ - public static boolean isStableVariable( + public static boolean isStableValue( @NotNull VariableDescriptor variableDescriptor, @Nullable ModuleDescriptor usageModule ) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt index afa606cb2be..8bafcd0abc9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/AssignedVariablesSearcher.kt @@ -16,25 +16,50 @@ package org.jetbrains.kotlin.types.expressions +import com.google.common.collect.LinkedHashMultimap +import com.google.common.collect.SetMultimap +import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.JetBinaryExpression -import org.jetbrains.kotlin.psi.JetNameReferenceExpression -import org.jetbrains.kotlin.psi.JetPsiUtil -import org.jetbrains.kotlin.psi.JetTreeVisitorVoid -import java.util.* +import org.jetbrains.kotlin.psi.* abstract class AssignedVariablesSearcher: JetTreeVisitorVoid() { - protected val assignedNames: MutableSet = LinkedHashSet() + private val assignedNames: SetMultimap = LinkedHashMultimap.create() + + public open fun writers(variableDescriptor: VariableDescriptor) = assignedNames[variableDescriptor.name] + + public fun hasWriters(variableDescriptor: VariableDescriptor) = writers(variableDescriptor).isNotEmpty() + + private var currentDeclaration: JetDeclaration? = null + + override fun visitDeclaration(declaration: JetDeclaration) { + val previous = currentDeclaration + if (declaration is JetDeclarationWithBody || declaration is JetClassOrObject) { + currentDeclaration = declaration + } + else if (declaration is JetClassInitializer) { + // Go to class declaration: init -> body -> class + currentDeclaration = declaration.parent.parent as JetDeclaration + } + super.visitDeclaration(declaration) + currentDeclaration = previous + } + + override fun visitFunctionLiteralExpression(functionLiteralExpression: JetFunctionLiteralExpression) { + val previous = currentDeclaration + currentDeclaration = functionLiteralExpression.functionLiteral + super.visitFunctionLiteralExpression(functionLiteralExpression) + currentDeclaration = previous + } override fun visitBinaryExpression(binaryExpression: JetBinaryExpression) { if (binaryExpression.operationToken === JetTokens.EQ) { val left = JetPsiUtil.deparenthesize(binaryExpression.left) if (left is JetNameReferenceExpression) { - assignedNames += left.getReferencedNameAsName() + assignedNames.put(left.getReferencedNameAsName(), currentDeclaration) } } + super.visitBinaryExpression(binaryExpression) } - } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index 656dd99f037..418009c12d4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -145,8 +145,8 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito // We can comment first part of this condition to take them into account, like here: var s: String? = "xyz" // In this case s will be not-nullable until it is changed if (property.getTypeReference() == null && type != null) { - DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue( - propertyDescriptor, context.trace.getBindingContext(), + DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValueForProperty( + property, propertyDescriptor, context.trace.getBindingContext(), DescriptorUtils.getContainingModuleOrNull(scope.getOwnerDescriptor())); DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context); // We cannot say here anything new about initializerDataFlowValue diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryDeclarationVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryDeclarationVisitor.kt new file mode 100644 index 00000000000..28e9be5d25a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryDeclarationVisitor.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2015 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.kotlin.types.expressions + +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.psi.JetDeclaration +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils + +class PreliminaryDeclarationVisitor(val declaration: JetDeclaration): AssignedVariablesSearcher() { + + override fun writers(variableDescriptor: VariableDescriptor): MutableSet { + lazyTrigger + return super.writers(variableDescriptor) + } + + private val lazyTrigger by lazy { + declaration.accept(this) + } + + companion object { + + fun createForExpression(expression: JetExpression, trace: BindingTrace) { + expression.getStrictParentOfType()?.let { createForDeclaration(it, trace) } + } + + fun createForDeclaration(declaration: JetDeclaration, trace: BindingTrace) { + // TODO: find top-most declaration (but not class!!!) + // TODO: check if already exists + val visitor = PreliminaryDeclarationVisitor(declaration) + // Can this declaration be synthetic? If yes, it would be better not to record it + trace.record(BindingContext.PRELIMINARY_VISITOR, declaration, visitor); + } + + fun getVisitorByVariable(variableDescriptor: VariableDescriptor, bindingContext: BindingContext): PreliminaryDeclarationVisitor? { + // Search for preliminary visitor of parent descriptor + val containingDescriptor = variableDescriptor.containingDeclaration + var currentDeclaration: JetDeclaration? = + DescriptorToSourceUtils.descriptorToDeclaration(containingDescriptor) as? JetDeclaration ?: return null + var preliminaryVisitor = bindingContext.get(BindingContext.PRELIMINARY_VISITOR, currentDeclaration) + while (preliminaryVisitor == null && currentDeclaration != null) { + currentDeclaration = currentDeclaration.getStrictParentOfType() + preliminaryVisitor = bindingContext.get(BindingContext.PRELIMINARY_VISITOR, currentDeclaration) + } + return preliminaryVisitor + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt index 14859e34bbf..885a0aa7004 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt @@ -36,7 +36,7 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher() // Only predictable variables are under interest here val id = value.id if (value.kind == DataFlowValue.Kind.PREDICTABLE_VARIABLE && id is LocalVariableDescriptor) { - if (assignedNames.contains(id.name)) { + if (hasWriters(id)) { valueSetToClear.add(value) } } diff --git a/compiler/testData/diagnostics/tests/infos/SmartCasts.kt b/compiler/testData/diagnostics/tests/infos/SmartCasts.kt index ceb0b43cd93..c38d54c7aef 100644 --- a/compiler/testData/diagnostics/tests/infos/SmartCasts.kt +++ b/compiler/testData/diagnostics/tests/infos/SmartCasts.kt @@ -211,9 +211,9 @@ fun f(): String { // a is a string, despite of being a variable val i: String = a a.compareTo("f") - // Beginning from here a is captured in a closure so we have to be cautious - val f: Function0 = { a } - return a + // Beginning from here a is captured in a closure but nobody modifies it + val f: Function0 = { a } + return a } return "" } diff --git a/compiler/testData/diagnostics/tests/script/varInScript.kt b/compiler/testData/diagnostics/tests/script/varInScript.kt new file mode 100644 index 00000000000..63f26ae316d --- /dev/null +++ b/compiler/testData/diagnostics/tests/script/varInScript.kt @@ -0,0 +1,6 @@ +// FILE: script.kts + +fun main(): Boolean { + var liteProfileReached = false + return liteProfileReached +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/script/varInScript.txt b/compiler/testData/diagnostics/tests/script/varInScript.txt new file mode 100644 index 00000000000..aba5a53c7d7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/script/varInScript.txt @@ -0,0 +1,10 @@ +package + +public final class Script { + public constructor Script() + public final val rv: kotlin.Unit + 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 final fun main(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.kt new file mode 100644 index 00000000000..5792a3f89a0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.kt @@ -0,0 +1,14 @@ +// !DIAGNOSTICS: -NOTHING_TO_INLINE + +inline fun foo(t1: T, t2: T) = t1 ?: t2 + +inline fun bar(l: (T) -> Unit): T = null!! + +fun use() { + var x: Int? + x = 5 + // Write is AFTER + x.hashCode() + // x is nullable at the second argument + foo(bar { x = null }, x!!) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.txt new file mode 100644 index 00000000000..b18e5d3bcf2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.txt @@ -0,0 +1,5 @@ +package + +@kotlin.inline() public fun bar(/*0*/ l: (T) -> kotlin.Unit): T +@kotlin.inline() public fun foo(/*0*/ t1: T, /*1*/ t2: T): T +public fun use(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.kt new file mode 100644 index 00000000000..0cbe4c4629b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.kt @@ -0,0 +1,14 @@ +// !DIAGNOSTICS: -NOTHING_TO_INLINE +// See KT-9143: smart cast on a variable nulled inside a lambda argument +inline fun foo(t1: T, t2: T) = t1 ?: t2 + +inline fun bar(l: (T) -> Unit): T = null!! + +fun use() { + var x: Int? + x = 5 + // Write to x is AFTER + x.hashCode() + // No smart cast should be here! + foo(bar { x = null }, x.hashCode()) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.txt new file mode 100644 index 00000000000..b18e5d3bcf2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.txt @@ -0,0 +1,5 @@ +package + +@kotlin.inline() public fun bar(/*0*/ l: (T) -> kotlin.Unit): T +@kotlin.inline() public fun foo(/*0*/ t1: T, /*1*/ t2: T): T +public fun use(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.kt new file mode 100644 index 00000000000..12638fe0246 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +fun run(f: () -> Unit) = 0 + +fun foo(arg: Int?) { + run { + var x = arg + if (x == null) return@run + x.hashCode() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.txt new file mode 100644 index 00000000000..af98b2de23f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit +public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.kt new file mode 100644 index 00000000000..77d75a3d730 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +fun run(f: () -> Unit) = 0 + +fun foo(arg: Int?) { + run { + var x = arg + while (x != null) { + x = x.hashCode() + if (x == 0) x = null + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.txt new file mode 100644 index 00000000000..af98b2de23f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit +public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.kt new file mode 100644 index 00000000000..f14e005f3e9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +fun run(f: () -> Unit) = 0 + +class My { + constructor(arg: Int?) { + run { + var x = arg + if (x == null) return@run + x.hashCode() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.txt new file mode 100644 index 00000000000..360894576d1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.txt @@ -0,0 +1,10 @@ +package + +public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int + +public final class My { + public constructor My(/*0*/ arg: kotlin.Int?) + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.kt new file mode 100644 index 00000000000..e121f76a88e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.kt @@ -0,0 +1,22 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +fun run(f: () -> Unit) = 0 + +fun foo(arg: Int?) = run { + var x = arg + if (x == null) return@run + x.hashCode() +} + +class My { + fun foo(arg: Int?) = run { + var x = arg + if (x == null) return@run + x.hashCode() + } + + fun Int?.bar() = run { + var x = this + if (x == null) return@run + x.hashCode() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.txt new file mode 100644 index 00000000000..ec4095500ea --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.txt @@ -0,0 +1,13 @@ +package + +public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Int +public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int + +public final class My { + public constructor My() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ arg: kotlin.Int?): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public final fun kotlin.Int?.bar(): kotlin.Int +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.kt b/compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.kt new file mode 100644 index 00000000000..957dfd8e27f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +fun run(f: () -> Unit) = 0 + +fun foo(arg: Int?) { + var x = arg + if (x == null) return + run { + // Not safe: x = null later in the owner + x.hashCode() + } + x = null +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.txt b/compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.txt new file mode 100644 index 00000000000..af98b2de23f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit +public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt b/compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt new file mode 100644 index 00000000000..b3748fde2c0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt @@ -0,0 +1,15 @@ +fun trans(n: Int, f: () -> Boolean) = if (f()) n else null + +fun foo() { + var i: Int? = 5 + if (i != null) { + class Changing { + fun bar() { + i = null + } + } + i.hashCode() + Changing().bar() + i.hashCode() + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/localClassChanges.txt b/compiler/testData/diagnostics/tests/smartCasts/localClassChanges.txt new file mode 100644 index 00000000000..0a5f0a339a7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localClassChanges.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int? diff --git a/compiler/testData/diagnostics/tests/smartCasts/localFunBetween.kt b/compiler/testData/diagnostics/tests/smartCasts/localFunBetween.kt new file mode 100644 index 00000000000..d5864d4ed89 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localFunBetween.kt @@ -0,0 +1,15 @@ +open class Base +class Derived: Base() + +fun bar(derived: Derived) = derived + +fun trans(n: Int, f: (Int) -> Boolean) = if (f(n)) n else null + +fun foo() { + val base: Base = Derived() + if (base is Derived) { + fun can(n: Int) = n > 0 + trans(42, ::can) + bar(base) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/localFunBetween.txt b/compiler/testData/diagnostics/tests/smartCasts/localFunBetween.txt new file mode 100644 index 00000000000..4e679ca6b74 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localFunBetween.txt @@ -0,0 +1,19 @@ +package + +public fun bar(/*0*/ derived: Derived): Derived +public fun foo(): kotlin.Unit +public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: (kotlin.Int) -> kotlin.Boolean): kotlin.Int? + +public open class Base { + public constructor Base() + 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 +} + +public final class Derived : Base { + public constructor Derived() + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/localFunChanges.kt b/compiler/testData/diagnostics/tests/smartCasts/localFunChanges.kt new file mode 100644 index 00000000000..c748e29156b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localFunChanges.kt @@ -0,0 +1,14 @@ +fun trans(n: Int, f: () -> Boolean) = if (f()) n else null + +fun foo() { + var i: Int? = 5 + if (i != null) { + fun can(): Boolean { + i = null + return true + } + i.hashCode() + trans(i, ::can) + i.hashCode() + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/localFunChanges.txt b/compiler/testData/diagnostics/tests/smartCasts/localFunChanges.txt new file mode 100644 index 00000000000..0a5f0a339a7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localFunChanges.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int? diff --git a/compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.kt b/compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.kt new file mode 100644 index 00000000000..1fd04697241 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.kt @@ -0,0 +1,15 @@ +fun trans(n: Int, f: () -> Boolean) = if (f()) n else null + +fun foo() { + var i: Int? = 5 + if (i != null) { + // Write is AFTER this place + i.hashCode() + object { + fun bar() { + i = null + } + }.bar() + i.hashCode() + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.txt b/compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.txt new file mode 100644 index 00000000000..0a5f0a339a7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int? diff --git a/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.kt b/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.kt new file mode 100644 index 00000000000..5c64093a0d4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.kt @@ -0,0 +1,15 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +fun run(f: () -> Unit) = 0 + +fun foo(arg: Int?) { + var x = arg + if (x == null) return + run { + // Unsafe because of owner modification + x.hashCode() + x = null + } + if (x != null) x = 42 + // Unsafe because of lambda + x.hashCode() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.txt b/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.txt new file mode 100644 index 00000000000..af98b2de23f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit +public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt b/compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt new file mode 100644 index 00000000000..957538072bb --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt @@ -0,0 +1,7 @@ +class My { + init { + var y: Int? + y = 42 + y.hashCode() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.txt b/compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.txt new file mode 100644 index 00000000000..c33a193c304 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.txt @@ -0,0 +1,8 @@ +package + +public final class My { + public constructor My() + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.kt b/compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.kt new file mode 100644 index 00000000000..d0390388a9a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.kt @@ -0,0 +1,10 @@ +fun foo() { + class My { + val x: Int + init { + var y: Int? + y = 42 + x = y.hashCode() + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.txt b/compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInAccessor.kt b/compiler/testData/diagnostics/tests/smartCasts/varInAccessor.kt new file mode 100644 index 00000000000..acc0c5bd52f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInAccessor.kt @@ -0,0 +1,17 @@ +var x: Int = 0 + get() { + var y: Int? = null + if (y != null) { + return y.hashCode() + } + return field + } + set(param) { + var y: Int? = null + if (y != null) { + field = y.hashCode() + } + else { + field = param + } + } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInAccessor.txt b/compiler/testData/diagnostics/tests/smartCasts/varInAccessor.txt new file mode 100644 index 00000000000..215ad162c81 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInAccessor.txt @@ -0,0 +1,3 @@ +package + +public var x: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInInitializer.kt b/compiler/testData/diagnostics/tests/smartCasts/varInInitializer.kt new file mode 100644 index 00000000000..24713f425bc --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInInitializer.kt @@ -0,0 +1,12 @@ +class My { + val x: Int + init { + var y: Int? = null + if (y != null) { + x = y.hashCode() + } + else { + x = 0 + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInInitializer.txt b/compiler/testData/diagnostics/tests/smartCasts/varInInitializer.txt new file mode 100644 index 00000000000..4bfaebfb9c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInInitializer.txt @@ -0,0 +1,9 @@ +package + +public final class My { + public constructor My() + public final val x: kotlin.Int + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.kt b/compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.kt new file mode 100644 index 00000000000..a06eda81077 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.kt @@ -0,0 +1,10 @@ +class My(val z: Int) { + var x: Int = 0 + constructor(arg: Int?): this(arg ?: 42) { + var y: Int? + y = arg + if (y != null) { + x = y + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.txt b/compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.txt new file mode 100644 index 00000000000..418a23940d5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.txt @@ -0,0 +1,11 @@ +package + +public final class My { + public constructor My(/*0*/ z: kotlin.Int) + public constructor My(/*0*/ arg: kotlin.Int?) + public final var x: kotlin.Int + public final val z: kotlin.Int + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.kt b/compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.kt new file mode 100644 index 00000000000..dea413f1678 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.kt @@ -0,0 +1,17 @@ +// Based on KT-8643 +public class MyClass +{ + fun main() { + var str: String? = null + + if (str != null) + callback { + // Nodoby writes str, smart cast is possible + method1(str) + } + } + + inline fun callback(foo: () ->Unit) = foo() + + fun method1(str: String) = str +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.txt b/compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.txt new file mode 100644 index 00000000000..88f3d7d050d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.txt @@ -0,0 +1,11 @@ +package + +public final class MyClass { + public constructor MyClass() + @kotlin.inline() public final fun callback(/*0*/ foo: () -> kotlin.Unit): kotlin.Unit + 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 final fun main(): kotlin.Unit + public final fun method1(/*0*/ str: kotlin.String): kotlin.String + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.kt new file mode 100644 index 00000000000..62feb9fe06f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.kt @@ -0,0 +1,23 @@ +class My { + + val y: Int + get() { + var x: Int? + x = 3 + return x.hashCode() + } + + fun test() { + var x: Int? + x = 2 + x.hashCode() + fun bb() { + var x: Any? + x = 4 + x.hashCode() + } + x = 4 + // Really smart cast is possible but name shadowing by bb() prevents it + x.hashCode() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.txt new file mode 100644 index 00000000000..9557624d2ef --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.txt @@ -0,0 +1,10 @@ +package + +public final class My { + public constructor My() + public final val y: kotlin.Int + 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 final fun test(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.kt new file mode 100644 index 00000000000..1a55a8f48cd --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.kt @@ -0,0 +1,18 @@ +// FILE: My.java + +public interface My { + String foo(String arg); +} + +// FILE: test.kt + +class Your { + val x = My() { + arg: String? -> + var y = arg + val z: String + if (y != null) z = y + else z = "42" + z + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.txt new file mode 100644 index 00000000000..1c1810c19c8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.txt @@ -0,0 +1,18 @@ +package + +public /*synthesized*/ fun My(/*0*/ function: (kotlin.String!) -> kotlin.String!): My + +public interface My { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(/*0*/ arg: kotlin.String!): kotlin.String! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Your { + public constructor Your() + public final val x: My + 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 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt new file mode 100644 index 00000000000..e47510d2f79 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt @@ -0,0 +1,9 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun foo(x: Int, f: () -> Unit, y: Int) {} + +fun bar() { + var x: Int? + x = 4 + foo(x, { x = null; x.hashCode() }, x) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.txt new file mode 100644 index 00000000000..2cb19118d5f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.txt @@ -0,0 +1,4 @@ +package + +public fun bar(): kotlin.Unit +public fun foo(/*0*/ x: kotlin.Int, /*1*/ f: () -> kotlin.Unit, /*2*/ y: kotlin.Int): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt index c91d59f95b8..139f60da941 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt @@ -9,7 +9,7 @@ public fun foo() { } if (s != null) { System.out.println(closure()) - // Smart cast is possible but closure makes it harder to understand - System.out.println(s.length) + // Smart cast is possible, nobody modifies s + System.out.println(s.length) } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.kt new file mode 100644 index 00000000000..4bec6488c4c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.kt @@ -0,0 +1,11 @@ +// KT-7186: False "Type mismatch" error + +fun indexOfMax(a: IntArray): Int? { + var maxI: Int? = null + a.forEachIndexed { i, value -> + if (maxI == null || value >= a[maxI]) { + maxI = i + } + } + return maxI +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.txt new file mode 100644 index 00000000000..72d4ed9252f --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.txt @@ -0,0 +1,3 @@ +package + +public fun indexOfMax(/*0*/ a: kotlin.IntArray): kotlin.Int? diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.kt new file mode 100644 index 00000000000..779a06a60e5 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.kt @@ -0,0 +1,14 @@ +// KT-7186: False "Type mismatch" error + +fun indexOfMax(a: IntArray): Int? { + var maxI: Int? = 0 + a.forEachIndexed { i, value -> + if (value >= a[maxI]) { + maxI = i + } + else if (value < 0) { + maxI = null + } + } + return maxI +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.txt new file mode 100644 index 00000000000..72d4ed9252f --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.txt @@ -0,0 +1,3 @@ +package + +public fun indexOfMax(/*0*/ a: kotlin.IntArray): kotlin.Int? diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt new file mode 100644 index 00000000000..c67bb7bc9c7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt @@ -0,0 +1,11 @@ +class My(val x: Int?) { + val y: Int? by lazy { + var z = x + while (z != null) { + z = z.hashCode() + if (z < 0) return@lazy z + if (z == 0) z = null + } + return@lazy null + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.txt new file mode 100644 index 00000000000..017fe5dd5a0 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.txt @@ -0,0 +1,10 @@ +package + +public final class My { + public constructor My(/*0*/ x: kotlin.Int?) + public final val x: kotlin.Int? + public final val y: kotlin.Int? + 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 +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.kt new file mode 100644 index 00000000000..137f43816f5 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.kt @@ -0,0 +1,7 @@ +// KT-9051: Allow smart cast for captured variables if they are not modified + +fun foo(y: String) { + var x: String? = null + y.let { x = it } + x.length // Smart cast is not possible +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.txt new file mode 100644 index 00000000000..4b62640d544 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ y: kotlin.String): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.kt new file mode 100644 index 00000000000..4d1925cb2ff --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.kt @@ -0,0 +1,7 @@ +// KT-9051: Allow smart cast for captured variables if they are not modified + +fun foo(y: String?) { + var x: String? = null + y?.let { x = it } + x.length // Smart cast is not possible +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.txt new file mode 100644 index 00000000000..19a74938e59 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.kt new file mode 100644 index 00000000000..d320fa3509a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.kt @@ -0,0 +1,9 @@ +// KT-9051: Allow smart cast for captured variables if they are not modified + +fun foo(y: String?) { + var x: String? = "" + if (x != null) { + y?.let { x = null } + x.length // Smart cast is not possible + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.txt new file mode 100644 index 00000000000..19a74938e59 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.kt new file mode 100644 index 00000000000..76d58492ea1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.kt @@ -0,0 +1,11 @@ +// KT-9051: Allow smart cast for captured variables if they are not modified + +fun bar(z: String?) = z + +fun foo(y: String?) { + var x: String? = "" + if (x != null) { + bar(y?.let { x = null; it }).length + x.length // Smart cast is not possible + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.txt new file mode 100644 index 00000000000..770f4843d13 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.txt @@ -0,0 +1,4 @@ +package + +public fun bar(/*0*/ z: kotlin.String?): kotlin.String? +public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.kt new file mode 100644 index 00000000000..be19cc5cfd9 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.kt @@ -0,0 +1,9 @@ +// KT-9051: Allow smart cast for captured variables if they are not modified + +fun foo(y: String?) { + var x: String? = null + if (x != null) { + y?.let { x = it } + x.length // not-null or not-null + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.txt new file mode 100644 index 00000000000..19a74938e59 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.kt new file mode 100644 index 00000000000..426fdd20c11 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.kt @@ -0,0 +1,10 @@ +// KT-9051: Allow smart cast for captured variables if they are not modified + +fun foo(y: String?) { + var x: String? = "" + if (x != null) { + y?.let { x != y } + // x is not changed, smart cast is possible + x.length + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.txt new file mode 100644 index 00000000000..19a74938e59 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.kt new file mode 100644 index 00000000000..ddaab04f303 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.kt @@ -0,0 +1,10 @@ +fun foo(y: String?) { + var x: String? = "" + if (x != null) { + with(y?.let { x = null; it }) { + this.length + x.length + } + x.length + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.txt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.txt new file mode 100644 index 00000000000..19a74938e59 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 231d7480ce5..df3a553f19c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -13441,6 +13441,21 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/script") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Script extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInScript() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/script"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("varInScript.kt") + public void testVarInScript() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/script/varInScript.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/sealed") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -14226,6 +14241,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("lambdaAndArgument.kt") + public void testLambdaAndArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgument.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaAndArgumentFun.kt") + public void testLambdaAndArgumentFun() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaAndArgumentFun.kt"); + doTest(fileName); + } + @TestMetadata("lambdaCall.kt") public void testLambdaCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaCall.kt"); @@ -14238,6 +14265,60 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("lambdaDeclaresAndModifies.kt") + public void testLambdaDeclaresAndModifies() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifies.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaDeclaresAndModifiesInLoop.kt") + public void testLambdaDeclaresAndModifiesInLoop() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInLoop.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaDeclaresAndModifiesInSecondary.kt") + public void testLambdaDeclaresAndModifiesInSecondary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesInSecondary.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaDeclaresAndModifiesWithDirectEq.kt") + public void testLambdaDeclaresAndModifiesWithDirectEq() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaDeclaresAndModifiesWithDirectEq.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaUsesOwnerModifies.kt") + public void testLambdaUsesOwnerModifies() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/lambdaUsesOwnerModifies.kt"); + doTest(fileName); + } + + @TestMetadata("localClassChanges.kt") + public void testLocalClassChanges() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/localClassChanges.kt"); + doTest(fileName); + } + + @TestMetadata("localFunBetween.kt") + public void testLocalFunBetween() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/localFunBetween.kt"); + doTest(fileName); + } + + @TestMetadata("localFunChanges.kt") + public void testLocalFunChanges() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/localFunChanges.kt"); + doTest(fileName); + } + + @TestMetadata("localObjectChanges.kt") + public void testLocalObjectChanges() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/localObjectChanges.kt"); + doTest(fileName); + } + @TestMetadata("noErrorCheckForPackageLevelVal.kt") public void testNoErrorCheckForPackageLevelVal() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/noErrorCheckForPackageLevelVal.kt"); @@ -14250,6 +14331,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("ownerDeclaresBothModifies.kt") + public void testOwnerDeclaresBothModifies() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/ownerDeclaresBothModifies.kt"); + doTest(fileName); + } + @TestMetadata("thisWithLabel.kt") public void testThisWithLabel() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/thisWithLabel.kt"); @@ -14262,6 +14349,42 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("varChangedInInitializer.kt") + public void testVarChangedInInitializer() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varChangedInInitializer.kt"); + doTest(fileName); + } + + @TestMetadata("varChangedInLocalInitializer.kt") + public void testVarChangedInLocalInitializer() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varChangedInLocalInitializer.kt"); + doTest(fileName); + } + + @TestMetadata("varInAccessor.kt") + public void testVarInAccessor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varInAccessor.kt"); + doTest(fileName); + } + + @TestMetadata("varInInitializer.kt") + public void testVarInInitializer() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varInInitializer.kt"); + doTest(fileName); + } + + @TestMetadata("varInSecondaryConstructor.kt") + public void testVarInSecondaryConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varInSecondaryConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("varInsideLocalFun.kt") + public void testVarInsideLocalFun() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varInsideLocalFun.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/inference") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -14917,6 +15040,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class Variables extends AbstractJetDiagnosticsTest { + @TestMetadata("accessorAndFunction.kt") + public void testAccessorAndFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/accessorAndFunction.kt"); + doTest(fileName); + } + public void testAllFilesPresentInVariables() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/variables"), Pattern.compile("^(.+)\\.kt$"), true); } @@ -14963,6 +15092,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("inPropertySam.kt") + public void testInPropertySam() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/inPropertySam.kt"); + doTest(fileName); + } + @TestMetadata("infix.kt") public void testInfix() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/infix.kt"); @@ -14981,6 +15116,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("lambdaBetweenArguments.kt") + public void testLambdaBetweenArguments() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt"); + doTest(fileName); + } + @TestMetadata("property.kt") public void testProperty() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/property.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index 1c8c5dd0136..8af739ff515 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -967,11 +967,71 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/smartcasts"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("forEachSafe.kt") + public void testForEachSafe() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachSafe.kt"); + doTest(fileName); + } + + @TestMetadata("forEachUnsafe.kt") + public void testForEachUnsafe() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/forEachUnsafe.kt"); + doTest(fileName); + } + + @TestMetadata("lazyDeclaresAndModifies.kt") + public void testLazyDeclaresAndModifies() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/lazyDeclaresAndModifies.kt"); + doTest(fileName); + } + + @TestMetadata("letAlwaysChangesToNotNull.kt") + public void testLetAlwaysChangesToNotNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letAlwaysChangesToNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("letChangesToNotNull.kt") + public void testLetChangesToNotNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("letChangesToNull.kt") + public void testLetChangesToNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNull.kt"); + doTest(fileName); + } + + @TestMetadata("letChangesToNullComplex.kt") + public void testLetChangesToNullComplex() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.kt"); + doTest(fileName); + } + + @TestMetadata("letMergeNotNull.kt") + public void testLetMergeNotNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letMergeNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("letStable.kt") + public void testLetStable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letStable.kt"); + doTest(fileName); + } + @TestMetadata("letUsesOwnReceiver.kt") public void testLetUsesOwnReceiver() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/letUsesOwnReceiver.kt"); doTest(fileName); } + + @TestMetadata("withChangesToNull.kt") + public void testWithChangesToNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/varargs") diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt index 77f388f1c39..865de792a21 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/analysis/AnalyzerUtil.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.types.expressions.JetTypeInfo +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor @JvmOverloads public fun JetExpression.computeTypeInfoInContext( @@ -39,6 +40,7 @@ public fun JetExpression.computeTypeInfoInContext( expectedType: JetType = TypeUtils.NO_EXPECTED_TYPE, isStatement: Boolean = false ): JetTypeInfo { + PreliminaryDeclarationVisitor.createForExpression(this, trace) return contextExpression.getResolutionFacade().frontendService() .getTypeInfo(scope.asLexicalScope(), this, expectedType, dataFlowInfo, trace, isStatement) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt index d272255dba2..dcb65af2572 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.utils.addImportScope import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import javax.inject.Inject @@ -55,6 +56,7 @@ public class CodeFragmentAnalyzer( resolveElementCache!!.resolveToElement(it, bodyResolveMode) } ?: return + PreliminaryDeclarationVisitor.createForExpression(codeFragmentExpression, trace) expressionTypingServices.getTypeInfo( scopeForContextElement, codeFragmentExpression, diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt index f8f8385e981..57dedcb6e5a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt @@ -135,5 +135,5 @@ fun JetExpression.isStableVariable(): Boolean { val context = this.analyze() val descriptor = BindingContextUtils.extractVariableDescriptorIfAny(context, this, false) return descriptor is VariableDescriptor && - DataFlowValueFactory.isStableVariable(descriptor, DescriptorUtils.getContainingModule(descriptor)) + DataFlowValueFactory.isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt index a3969098d2a..0c67b36e324 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt @@ -49,6 +49,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.asLexicalScope import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -230,6 +231,7 @@ object ReplaceWithAnnotationAnalyzer { else { resolutionFacade.getFrontendService(module, ExpressionTypingServices::class.java) } + PreliminaryDeclarationVisitor.createForExpression(expression, traceContext) frontendService.getTypeInfo(scope, expression, TypeUtils.NO_EXPECTED_TYPE, DataFlowInfo.EMPTY, traceContext, false) return traceContext.bindingContext } diff --git a/idea/testData/checker/infos/SmartCasts.kt b/idea/testData/checker/infos/SmartCasts.kt index a656974aa85..98c6680ce1e 100644 --- a/idea/testData/checker/infos/SmartCasts.kt +++ b/idea/testData/checker/infos/SmartCasts.kt @@ -212,7 +212,10 @@ fun f(): String { if (a is String) { val i: String = a a.compareTo("f") - val f: Function0 = { a } + val f: Function0 = { + a = 42 + a + } return a } return ""