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:
@@ -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");
|
||||
|
||||
+111
-25
@@ -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
|
||||
) {
|
||||
|
||||
+33
-8
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
|
||||
+65
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
@@ -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
|
||||
+12
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
+12
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -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
|
||||
}
|
||||
+22
@@ -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()
|
||||
}
|
||||
}
|
||||
+13
@@ -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
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fun foo() {
|
||||
class My {
|
||||
val x: Int
|
||||
init {
|
||||
var y: Int?
|
||||
y = 42
|
||||
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -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
|
||||
}
|
||||
+23
@@ -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()
|
||||
}
|
||||
}
|
||||
+10
@@ -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
|
||||
}
|
||||
+9
@@ -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<!>)
|
||||
}
|
||||
+4
@@ -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
|
||||
+2
-2
@@ -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?
|
||||
+11
@@ -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
|
||||
}
|
||||
}
|
||||
+10
@@ -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
|
||||
}
|
||||
+7
@@ -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
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ y: kotlin.String): kotlin.Unit
|
||||
+7
@@ -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
|
||||
}
|
||||
+3
@@ -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
|
||||
+11
@@ -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
|
||||
}
|
||||
}
|
||||
+4
@@ -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
|
||||
+10
@@ -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
|
||||
}
|
||||
}
|
||||
+3
@@ -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");
|
||||
|
||||
+60
@@ -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)
|
||||
}
|
||||
|
||||
+2
@@ -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,
|
||||
|
||||
+1
-1
@@ -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))
|
||||
}
|
||||
|
||||
+2
@@ -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
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user