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
This commit is contained in:
Mikhail Glukhikh
2015-09-28 17:18:19 +03:00
parent de7d2978db
commit 4b35e3b135
84 changed files with 1047 additions and 43 deletions
@@ -310,3 +310,5 @@ public fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUt
public fun <E : PsiElement> E.createSmartPointer(): SmartPsiElementPointer<E> =
SmartPointerManager.getInstance(getProject()).createSmartPsiElementPointer(this)
public fun PsiElement.before(element: PsiElement) = textRange.endOffset <= element.textRange.startOffset
@@ -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<JetElement, Boolean> UNREACHABLE_CODE = Slices.createSimpleSetSlice();
WritableSlice<VariableDescriptor, CaptureKind> CAPTURED_IN_CLOSURE = new BasicWritableSlice<VariableDescriptor, CaptureKind>(DO_NOTHING);
WritableSlice<JetDeclaration, PreliminaryDeclarationVisitor> PRELIMINARY_VISITOR = new BasicWritableSlice<JetDeclaration, PreliminaryDeclarationVisitor>(DO_NOTHING);
WritableSlice<CallableMemberDescriptor, Boolean> NEED_SYNTHETIC_ACCESSOR = new BasicWritableSlice<CallableMemberDescriptor, Boolean>(DO_NOTHING);
@@ -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<LexicalScope, DataFlowInfo> beforeBlockBody,
@NotNull CallChecker callChecker
) {
PreliminaryDeclarationVisitor.Companion.createForDeclaration(function, trace);
LexicalScope innerScope = FunctionDescriptorUtil.getFunctionInnerScope(scope, functionDescriptor, trace);
List<JetParameter> valueParameters = function.getValueParameters();
List<ValueParameterDescriptor> valueParameterDescriptors = functionDescriptor.getValueParameters();
@@ -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<JetType>() {
@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);
@@ -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)
}
@@ -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");
@@ -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 <T : Any?> 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<JetDeclaration> 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<JetDeclaration> 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
) {
@@ -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<Name> = LinkedHashSet()
private val assignedNames: SetMultimap<Name, JetDeclaration?> = 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)
}
}
@@ -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
@@ -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<JetDeclaration?> {
lazyTrigger
return super.writers(variableDescriptor)
}
private val lazyTrigger by lazy {
declaration.accept(this)
}
companion object {
fun createForExpression(expression: JetExpression, trace: BindingTrace) {
expression.getStrictParentOfType<JetDeclaration>()?.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
}
}
}
@@ -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)
}
}
+3 -3
View File
@@ -211,9 +211,9 @@ fun f(): String {
// a is a string, despite of being a variable
val <!UNUSED_VARIABLE!>i<!>: String = <!DEBUG_INFO_SMARTCAST!>a<!>
<!DEBUG_INFO_SMARTCAST!>a<!>.compareTo("f")
// Beginning from here a is captured in a closure so we have to be cautious
val <!UNUSED_VARIABLE!>f<!>: Function0<String> = { <!SMARTCAST_IMPOSSIBLE!>a<!> }
return <!SMARTCAST_IMPOSSIBLE!>a<!>
// Beginning from here a is captured in a closure but nobody modifies it
val <!UNUSED_VARIABLE!>f<!>: Function0<String> = { <!DEBUG_INFO_SMARTCAST!>a<!> }
return <!DEBUG_INFO_SMARTCAST!>a<!>
}
return ""
}
@@ -0,0 +1,6 @@
// FILE: script.kts
fun main(): Boolean {
var liteProfileReached = false
return liteProfileReached
}
@@ -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
}
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -NOTHING_TO_INLINE
inline fun <T> foo(t1: T, t2: T) = t1 ?: t2
inline fun <T> bar(<!UNUSED_PARAMETER!>l<!>: (T) -> Unit): T = null!!
fun use() {
var x: Int?
x = 5
// Write is AFTER
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
// x is nullable at the second argument
foo(bar { x = null }, x!!)
}
@@ -0,0 +1,5 @@
package
@kotlin.inline() public fun </*0*/ T> bar(/*0*/ l: (T) -> kotlin.Unit): T
@kotlin.inline() public fun </*0*/ T> foo(/*0*/ t1: T, /*1*/ t2: T): T
public fun use(): kotlin.Unit
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -NOTHING_TO_INLINE
// See KT-9143: smart cast on a variable nulled inside a lambda argument
inline fun <T> foo(t1: T, t2: T) = t1 ?: t2
inline fun <T> bar(<!UNUSED_PARAMETER!>l<!>: (T) -> Unit): T = null!!
fun use() {
var x: Int?
x = 5
// Write to x is AFTER
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
// No smart cast should be here!
foo(bar { x = null }, <!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode())
}
@@ -0,0 +1,5 @@
package
@kotlin.inline() public fun </*0*/ T> bar(/*0*/ l: (T) -> kotlin.Unit): T
@kotlin.inline() public fun </*0*/ T> foo(/*0*/ t1: T, /*1*/ t2: T): T
public fun use(): kotlin.Unit
@@ -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
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -0,0 +1,12 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
fun foo(arg: Int?) {
run {
var x = arg
while (x != null) {
x = <!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
if (x == 0) x = null
}
}
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -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
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
}
}
@@ -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
}
@@ -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
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
class My {
fun foo(arg: Int?) = run {
var x = arg
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
fun Int?.bar() = run {
var x = this
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
}
@@ -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
}
@@ -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<!UNSAFE_CALL!>.<!>hashCode()
}
x = null
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -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<!UNSAFE_CALL!>.<!>hashCode()
Changing().bar()
i<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
@@ -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(<!DEBUG_INFO_SMARTCAST!>base<!>)
}
}
@@ -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
}
@@ -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<!UNSAFE_CALL!>.<!>hashCode()
trans(<!TYPE_MISMATCH!>i<!>, ::can)
i<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
@@ -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
<!DEBUG_INFO_SMARTCAST!>i<!>.hashCode()
object {
fun bar() {
i = null
}
}.bar()
i<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
@@ -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<!UNSAFE_CALL!>.<!>hashCode()
x = null
}
if (x != null) x = 42
// Unsafe because of lambda
x<!UNSAFE_CALL!>.<!>hashCode()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -0,0 +1,7 @@
class My {
init {
var y: Int?
y = 42
<!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
}
@@ -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
}
@@ -0,0 +1,10 @@
fun foo() {
class My {
val x: Int
init {
var y: Int?
y = 42
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -0,0 +1,17 @@
var x: Int = 0
get() {
var y: Int? = null
if (y != null) {
return <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
return field
}
set(param) {
var y: Int? = null
if (y != null) {
field = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
else {
field = param
}
}
@@ -0,0 +1,3 @@
package
public var x: kotlin.Int
@@ -0,0 +1,12 @@
class My {
val x: Int
init {
var y: Int? = null
if (y != null) {
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
else {
x = 0
}
}
}
@@ -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
}
@@ -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 = <!DEBUG_INFO_SMARTCAST!>y<!>
}
}
}
@@ -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
}
@@ -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(<!DEBUG_INFO_SMARTCAST!>str<!>)
}
}
inline fun callback(foo: () ->Unit) = foo()
fun method1(str: String) = str
}
@@ -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
}
@@ -0,0 +1,23 @@
class My {
val y: Int
get() {
var x: Int?
x = 3
return <!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
fun test() {
var x: Int?
x = 2
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
fun bb() {
var <!NAME_SHADOWING!>x<!>: Any?
x = 4
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
x = 4
// Really smart cast is possible but name shadowing by bb() prevents it
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
}
@@ -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
}
@@ -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 = <!DEBUG_INFO_SMARTCAST!>y<!>
else z = "42"
z
}
}
@@ -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
}
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() {
var x: Int?
x = 4
foo(<!DEBUG_INFO_SMARTCAST!>x<!>, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, <!SMARTCAST_IMPOSSIBLE!>x<!>)
}
@@ -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
@@ -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<!UNSAFE_CALL!>.<!>length)
// Smart cast is possible, nobody modifies s
System.out.println(<!DEBUG_INFO_SMARTCAST!>s<!>.length)
}
}
@@ -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[<!TYPE_MISMATCH!>maxI<!>]) {
maxI = i
}
}
return maxI
}
@@ -0,0 +1,3 @@
package
public fun indexOfMax(/*0*/ a: kotlin.IntArray): kotlin.Int?
@@ -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[<!TYPE_MISMATCH!>maxI<!>]) {
maxI = i
}
else if (value < 0) {
maxI = null
}
}
return maxI
}
@@ -0,0 +1,3 @@
package
public fun indexOfMax(/*0*/ a: kotlin.IntArray): kotlin.Int?
@@ -0,0 +1,11 @@
class My(val x: Int?) {
val y: Int? by lazy {
var z = x
while (z != null) {
z = <!DEBUG_INFO_SMARTCAST!>z<!>.hashCode()
if (<!DEBUG_INFO_SMARTCAST!>z<!> < 0) return@lazy z
if (z == 0) z = null
}
return@lazy null
}
}
@@ -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
}
@@ -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<!UNSAFE_CALL!>.<!>length // Smart cast is not possible
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ y: kotlin.String): kotlin.Unit
@@ -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<!UNSAFE_CALL!>.<!>length // Smart cast is not possible
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit
@@ -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<!UNSAFE_CALL!>.<!>length // Smart cast is not possible
}
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit
@@ -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 })<!UNSAFE_CALL!>.<!>length
x<!UNSAFE_CALL!>.<!>length // Smart cast is not possible
}
}
@@ -0,0 +1,4 @@
package
public fun bar(/*0*/ z: kotlin.String?): kotlin.String?
public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit
@@ -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<!UNSAFE_CALL!>.<!>length // not-null or not-null
}
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit
@@ -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
<!DEBUG_INFO_SMARTCAST!>x<!>.length
}
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit
@@ -0,0 +1,10 @@
fun foo(y: String?) {
var x: String? = ""
if (x != null) {
with(y?.let { x = null; it }) {
this<!UNSAFE_CALL!>.<!>length
x<!UNSAFE_CALL!>.<!>length
}
x<!UNSAFE_CALL!>.<!>length
}
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ y: kotlin.String?): kotlin.Unit
@@ -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");
@@ -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")
@@ -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<ExpressionTypingServices>()
.getTypeInfo(scope.asLexicalScope(), this, expectedType, dataFlowInfo, trace, isStatement)
}
@@ -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,
@@ -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))
}
@@ -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
}
+4 -1
View File
@@ -212,7 +212,10 @@ fun f(): String {
if (<info>a</info> is String) {
val <warning>i</warning>: String = <info descr="Smart cast to kotlin.String"><info descr="Wrapped into a reference object to be modified when captured in a closure">a</info></info>
<info descr="Smart cast to kotlin.String"><info descr="Wrapped into a reference object to be modified when captured in a closure">a</info></info>.compareTo("f")
val <warning>f</warning>: Function0<String> = { <error descr="[SMARTCAST_IMPOSSIBLE] Smart cast to 'kotlin.String' is impossible, because 'a' could have changed since the is-check">a</error> }
val <warning>f</warning>: Function0<String> = {
<info>a</info> = 42
<error descr="[TYPE_MISMATCH] Type mismatch: inferred type is kotlin.Any but kotlin.String was expected">a</error>
}
return <error descr="[SMARTCAST_IMPOSSIBLE] Smart cast to 'kotlin.String' is impossible, because 'a' could have changed since the is-check">a</error>
}
return ""