DataFlowValueFactory: converted to Kotlin
This commit is contained in:
committed by
Mikhail Glukhikh
parent
b1a2a0723c
commit
7f9b9ddb45
+278
-350
@@ -14,90 +14,72 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.smartcasts;
|
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||||
|
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair
|
||||||
import com.intellij.psi.PsiElement;
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import com.intellij.psi.tree.IElementType;
|
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.KtNodeTypes;
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider;
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind;
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
|
||||||
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 org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
|
||||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing;
|
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR;
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue.Kind.*
|
||||||
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.
|
* This class is intended to create data flow values for different kind of expressions.
|
||||||
* Then data flow values serve as keys to obtain data flow information for these expressions.
|
* Then data flow values serve as keys to obtain data flow information for these expressions.
|
||||||
*/
|
*/
|
||||||
public class DataFlowValueFactory {
|
object DataFlowValueFactory {
|
||||||
private DataFlowValueFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@JvmStatic
|
||||||
public static DataFlowValue createDataFlowValue(
|
fun createDataFlowValue(
|
||||||
@NotNull KtExpression expression,
|
expression: KtExpression,
|
||||||
@NotNull KotlinType type,
|
type: KotlinType,
|
||||||
@NotNull ResolutionContext resolutionContext
|
resolutionContext: ResolutionContext<*>
|
||||||
) {
|
) = createDataFlowValue(expression, type, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||||
return createDataFlowValue(expression, type, resolutionContext.trace.getBindingContext(),
|
|
||||||
resolutionContext.scope.getOwnerDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isComplexExpression(@NotNull KtExpression expression) {
|
private fun isComplexExpression(expression: KtExpression): Boolean = when(expression) {
|
||||||
if (expression instanceof KtBlockExpression ||
|
is KtBlockExpression, is KtIfExpression, is KtWhenExpression -> true
|
||||||
expression instanceof KtIfExpression ||
|
is KtBinaryExpression -> expression.operationToken === KtTokens.ELVIS
|
||||||
expression instanceof KtWhenExpression ||
|
is KtParenthesizedExpression -> {
|
||||||
(expression instanceof KtBinaryExpression && ((KtBinaryExpression) expression).getOperationToken() == KtTokens.ELVIS)) {
|
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||||
|
deparenthesized != null && isComplexExpression(deparenthesized)
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (expression instanceof KtParenthesizedExpression) {
|
else -> false
|
||||||
KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression);
|
|
||||||
return deparenthesized != null && isComplexExpression(deparenthesized);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@JvmStatic
|
||||||
public static DataFlowValue createDataFlowValue(
|
fun createDataFlowValue(
|
||||||
@NotNull KtExpression expression,
|
expression: KtExpression,
|
||||||
@NotNull KotlinType type,
|
type: KotlinType,
|
||||||
@NotNull BindingContext bindingContext,
|
bindingContext: BindingContext,
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule
|
containingDeclarationOrModule: DeclarationDescriptor
|
||||||
) {
|
): DataFlowValue {
|
||||||
if (expression instanceof KtConstantExpression) {
|
if (expression is KtConstantExpression) {
|
||||||
KtConstantExpression constantExpression = (KtConstantExpression) expression;
|
if (expression.node.elementType === KtNodeTypes.NULL) {
|
||||||
if (constantExpression.getNode().getElementType() == KtNodeTypes.NULL) {
|
return DataFlowValue.nullValue(containingDeclarationOrModule.builtIns)
|
||||||
return DataFlowValue.nullValue(DescriptorUtilsKt.getBuiltIns(containingDeclarationOrModule));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type.isError()) return DataFlowValue.ERROR;
|
if (type.isError) return DataFlowValue.ERROR
|
||||||
if (isNullableNothing(type)) {
|
if (isNullableNothing(type)) {
|
||||||
return DataFlowValue.nullValue(DescriptorUtilsKt.getBuiltIns(containingDeclarationOrModule)); // 'null' is the only inhabitant of 'Nothing?'
|
return DataFlowValue.nullValue(containingDeclarationOrModule.builtIns) // 'null' is the only inhabitant of 'Nothing?'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExpressionTypingUtils.isExclExclExpression(KtPsiUtil.deparenthesize(expression))) {
|
if (ExpressionTypingUtils.isExclExclExpression(KtPsiUtil.deparenthesize(expression))) {
|
||||||
@@ -106,368 +88,314 @@ public class DataFlowValueFactory {
|
|||||||
//
|
//
|
||||||
// But there are some problem with types built on type parameters, e.g.
|
// But there are some problem with types built on type parameters, e.g.
|
||||||
// fun <T : Any?> foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable
|
// 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,
|
return DataFlowValue(expression,
|
||||||
type,
|
type,
|
||||||
OTHER,
|
OTHER,
|
||||||
Nullability.NOT_NULL);
|
Nullability.NOT_NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isComplexExpression(expression)) {
|
if (isComplexExpression(expression)) {
|
||||||
return createDataFlowValueForComplexExpression(expression, type);
|
return createDataFlowValueForComplexExpression(expression, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule);
|
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
|
||||||
return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id,
|
return DataFlowValue(if (result === NO_IDENTIFIER_INFO) expression else result.id,
|
||||||
type,
|
type,
|
||||||
result.kind,
|
result.kind,
|
||||||
getImmanentNullability(type));
|
type.immanentNullability)
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@JvmStatic
|
||||||
public static DataFlowValue createDataFlowValueForStableReceiver(@NotNull ReceiverValue receiver) {
|
fun createDataFlowValueForStableReceiver(receiver: ReceiverValue): DataFlowValue {
|
||||||
KotlinType type = receiver.getType();
|
val type = receiver.type
|
||||||
return new DataFlowValue(receiver, type, STABLE_VALUE, getImmanentNullability(type));
|
return DataFlowValue(receiver, type, STABLE_VALUE, type.immanentNullability)
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@JvmStatic
|
||||||
public static DataFlowValue createDataFlowValue(
|
fun createDataFlowValue(
|
||||||
@NotNull ReceiverValue receiverValue,
|
receiverValue: ReceiverValue,
|
||||||
@NotNull ResolutionContext resolutionContext
|
resolutionContext: ResolutionContext<*>
|
||||||
) {
|
) = createDataFlowValue(receiverValue, resolutionContext.trace.bindingContext, resolutionContext.scope.ownerDescriptor)
|
||||||
return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(),
|
|
||||||
resolutionContext.scope.getOwnerDescriptor());
|
@JvmStatic
|
||||||
|
fun createDataFlowValue(
|
||||||
|
receiverValue: ReceiverValue,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
containingDeclarationOrModule: DeclarationDescriptor
|
||||||
|
) = when (receiverValue) {
|
||||||
|
is TransientReceiver, is ImplicitReceiver -> createDataFlowValueForStableReceiver(receiverValue)
|
||||||
|
is ExpressionReceiver -> createDataFlowValue(receiverValue.expression,
|
||||||
|
receiverValue.getType(),
|
||||||
|
bindingContext,
|
||||||
|
containingDeclarationOrModule)
|
||||||
|
else -> throw UnsupportedOperationException("Unsupported receiver value: " + receiverValue.javaClass.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@JvmStatic
|
||||||
public static DataFlowValue createDataFlowValue(
|
fun createDataFlowValueForProperty(
|
||||||
@NotNull ReceiverValue receiverValue,
|
property: KtProperty,
|
||||||
@NotNull BindingContext bindingContext,
|
variableDescriptor: VariableDescriptor,
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule
|
bindingContext: BindingContext,
|
||||||
) {
|
usageContainingModule: ModuleDescriptor?
|
||||||
if (receiverValue instanceof TransientReceiver || receiverValue instanceof ImplicitReceiver) {
|
): DataFlowValue {
|
||||||
return createDataFlowValueForStableReceiver(receiverValue);
|
val type = variableDescriptor.type
|
||||||
}
|
return DataFlowValue(variableDescriptor, type,
|
||||||
else if (receiverValue instanceof ExpressionReceiver) {
|
variableKind(variableDescriptor, usageContainingModule,
|
||||||
return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(),
|
bindingContext, property),
|
||||||
receiverValue.getType(),
|
type.immanentNullability)
|
||||||
bindingContext,
|
|
||||||
containingDeclarationOrModule);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun createDataFlowValueForComplexExpression(
|
||||||
public static DataFlowValue createDataFlowValueForProperty(
|
expression: KtExpression,
|
||||||
@NotNull KtProperty property,
|
type: KotlinType
|
||||||
@NotNull VariableDescriptor variableDescriptor,
|
) = DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, type.immanentNullability)
|
||||||
@NotNull BindingContext bindingContext,
|
|
||||||
@Nullable ModuleDescriptor usageContainingModule
|
private val KotlinType.immanentNullability: Nullability
|
||||||
) {
|
get() = if (TypeUtils.isNullableType(this)) Nullability.UNKNOWN else Nullability.NOT_NULL
|
||||||
KotlinType type = variableDescriptor.getType();
|
|
||||||
return new DataFlowValue(variableDescriptor, type,
|
private open class IdentifierInfo internal constructor(val id: Any?, val kind: Kind, val isPackage: Boolean)
|
||||||
variableKind(variableDescriptor, usageContainingModule,
|
|
||||||
bindingContext, property),
|
private val NO_IDENTIFIER_INFO = object : IdentifierInfo(null, OTHER, false) {
|
||||||
getImmanentNullability(type));
|
override fun toString() = "NO_IDENTIFIER_INFO"
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun createInfo(id: Any, kind: Kind) = IdentifierInfo(id, kind, false)
|
||||||
private static DataFlowValue createDataFlowValueForComplexExpression(
|
|
||||||
@NotNull KtExpression expression,
|
|
||||||
@NotNull KotlinType type
|
|
||||||
) {
|
|
||||||
return new DataFlowValue(expression, type, Kind.STABLE_COMPLEX_EXPRESSION, getImmanentNullability(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
private fun createStableInfo(id: Any) = createInfo(id, STABLE_VALUE)
|
||||||
private static Nullability getImmanentNullability(@NotNull KotlinType type) {
|
|
||||||
return TypeUtils.isNullableType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class IdentifierInfo {
|
private fun createPackageOrClassInfo(id: Any) = IdentifierInfo(id, STABLE_VALUE, true)
|
||||||
public final Object id;
|
|
||||||
public final Kind kind;
|
|
||||||
public final boolean isPackage;
|
|
||||||
|
|
||||||
private IdentifierInfo(Object id, Kind kind, boolean isPackage) {
|
private fun combineInfo(receiverInfo: IdentifierInfo?, selectorInfo: IdentifierInfo) =
|
||||||
this.id = id;
|
if (selectorInfo.id == null || receiverInfo === NO_IDENTIFIER_INFO) {
|
||||||
this.kind = kind;
|
NO_IDENTIFIER_INFO
|
||||||
this.isPackage = isPackage;
|
}
|
||||||
}
|
else if (receiverInfo == null || receiverInfo.isPackage) {
|
||||||
}
|
selectorInfo
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createInfo(Pair.create<Any, Any>(receiverInfo.id, selectorInfo.id),
|
||||||
|
if (receiverInfo.kind.isStable()) selectorInfo.kind else OTHER)
|
||||||
|
}
|
||||||
|
|
||||||
private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, OTHER, false) {
|
private fun createPostfixInfo(expression: KtPostfixExpression, argumentInfo: IdentifierInfo) =
|
||||||
@Override
|
if (argumentInfo === NO_IDENTIFIER_INFO) {
|
||||||
public String toString() {
|
NO_IDENTIFIER_INFO
|
||||||
return "NO_IDENTIFIER_INFO";
|
}
|
||||||
}
|
else {
|
||||||
};
|
createInfo(Pair.create<KtPostfixExpression, Any>(expression, argumentInfo.id), argumentInfo.kind)
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun getIdForStableIdentifier(
|
||||||
private static IdentifierInfo createInfo(Object id, Kind kind) {
|
expression: KtExpression?,
|
||||||
return new IdentifierInfo(id, kind, false);
|
bindingContext: BindingContext,
|
||||||
}
|
containingDeclarationOrModule: DeclarationDescriptor
|
||||||
|
): IdentifierInfo {
|
||||||
@NotNull
|
|
||||||
private static IdentifierInfo createStableInfo(Object id) {
|
|
||||||
return createInfo(id, STABLE_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static IdentifierInfo createPackageOrClassInfo(Object id) {
|
|
||||||
return new IdentifierInfo(id, STABLE_VALUE, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
|
|
||||||
if (selectorInfo.id == null || receiverInfo == NO_IDENTIFIER_INFO) {
|
|
||||||
return NO_IDENTIFIER_INFO;
|
|
||||||
}
|
|
||||||
if (receiverInfo == null || receiverInfo.isPackage) {
|
|
||||||
return selectorInfo;
|
|
||||||
}
|
|
||||||
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
|
|
||||||
receiverInfo.kind.isStable() ? selectorInfo.kind : OTHER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static IdentifierInfo createPostfixInfo(@NotNull KtPostfixExpression expression, @NotNull IdentifierInfo argumentInfo) {
|
|
||||||
if (argumentInfo == NO_IDENTIFIER_INFO) {
|
|
||||||
return NO_IDENTIFIER_INFO;
|
|
||||||
}
|
|
||||||
return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static IdentifierInfo getIdForStableIdentifier(
|
|
||||||
@Nullable KtExpression expression,
|
|
||||||
@NotNull BindingContext bindingContext,
|
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule
|
|
||||||
) {
|
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression);
|
val deparenthesized = KtPsiUtil.deparenthesize(expression)
|
||||||
if (expression != deparenthesized) {
|
if (expression !== deparenthesized) {
|
||||||
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule);
|
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (expression instanceof KtQualifiedExpression) {
|
return when (expression) {
|
||||||
KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) expression;
|
is KtQualifiedExpression -> {
|
||||||
KtExpression receiverExpression = qualifiedExpression.getReceiverExpression();
|
val receiverExpression = expression.receiverExpression
|
||||||
KtExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
val selectorExpression = expression.selectorExpression
|
||||||
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule);
|
val receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
|
||||||
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule);
|
val selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
|
||||||
|
|
||||||
return combineInfo(receiverId, selectorId);
|
combineInfo(receiverId, selectorId)
|
||||||
}
|
|
||||||
if (expression instanceof KtSimpleNameExpression) {
|
|
||||||
return getIdForSimpleNameExpression((KtSimpleNameExpression) expression, bindingContext, containingDeclarationOrModule);
|
|
||||||
}
|
|
||||||
else if (expression instanceof KtThisExpression) {
|
|
||||||
KtThisExpression thisExpression = (KtThisExpression) expression;
|
|
||||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
|
|
||||||
|
|
||||||
return getIdForThisReceiver(declarationDescriptor);
|
|
||||||
}
|
|
||||||
else if (expression instanceof KtPostfixExpression) {
|
|
||||||
KtPostfixExpression postfixExpression = (KtPostfixExpression) expression;
|
|
||||||
IElementType operationType = postfixExpression.getOperationReference().getReferencedNameElementType();
|
|
||||||
if (operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS) {
|
|
||||||
return createPostfixInfo(postfixExpression,
|
|
||||||
getIdForStableIdentifier(postfixExpression.getBaseExpression(), bindingContext, containingDeclarationOrModule));
|
|
||||||
}
|
}
|
||||||
}
|
is KtSimpleNameExpression ->
|
||||||
return NO_IDENTIFIER_INFO;
|
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
|
||||||
}
|
is KtThisExpression -> {
|
||||||
|
val declarationDescriptor = bindingContext.get(REFERENCE_TARGET, expression.instanceReference)
|
||||||
@NotNull
|
getIdForThisReceiver(declarationDescriptor)
|
||||||
private static IdentifierInfo getIdForSimpleNameExpression(
|
}
|
||||||
@NotNull KtSimpleNameExpression simpleNameExpression,
|
is KtPostfixExpression -> {
|
||||||
@NotNull BindingContext bindingContext,
|
val operationType = expression.operationReference.getReferencedNameElementType()
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule
|
if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS) {
|
||||||
) {
|
createPostfixInfo(expression,
|
||||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule))
|
||||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
}
|
||||||
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(simpleNameExpression, bindingContext);
|
else {
|
||||||
|
NO_IDENTIFIER_INFO
|
||||||
// todo uncomment assert
|
}
|
||||||
// KT-4113
|
}
|
||||||
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
|
else -> NO_IDENTIFIER_INFO
|
||||||
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
|
|
||||||
ModuleDescriptor usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule);
|
|
||||||
IdentifierInfo receiverInfo =
|
|
||||||
resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
|
|
||||||
|
|
||||||
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
|
||||||
return combineInfo(receiverInfo,
|
|
||||||
createInfo(variableDescriptor,
|
|
||||||
variableKind(variableDescriptor, usageModuleDescriptor,
|
|
||||||
bindingContext, simpleNameExpression)));
|
|
||||||
}
|
|
||||||
if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) {
|
|
||||||
return createPackageOrClassInfo(declarationDescriptor);
|
|
||||||
}
|
|
||||||
return NO_IDENTIFIER_INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static IdentifierInfo getIdForImplicitReceiver(@Nullable ReceiverValue receiverValue, @Nullable KtExpression expression) {
|
|
||||||
if (receiverValue instanceof ImplicitReceiver) {
|
|
||||||
return getIdForThisReceiver(((ImplicitReceiver) receiverValue).getDeclarationDescriptor());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert !(receiverValue instanceof TransientReceiver)
|
|
||||||
: "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiverValue;
|
|
||||||
// For ExpressionReceiver there is an explicit "this" expression and it was analyzed earlier
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun getIdForSimpleNameExpression(
|
||||||
private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
|
simpleNameExpression: KtSimpleNameExpression,
|
||||||
if (descriptorOfThisReceiver instanceof CallableDescriptor) {
|
bindingContext: BindingContext,
|
||||||
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
|
containingDeclarationOrModule: DeclarationDescriptor
|
||||||
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " +
|
): IdentifierInfo {
|
||||||
descriptorOfThisReceiver;
|
val declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression)
|
||||||
return createStableInfo(receiverParameter.getValue());
|
return when (declarationDescriptor) {
|
||||||
|
is VariableDescriptor -> {
|
||||||
|
val resolvedCall = simpleNameExpression.getResolvedCall(bindingContext)
|
||||||
|
|
||||||
|
// todo uncomment assert
|
||||||
|
// KT-4113
|
||||||
|
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
|
||||||
|
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
|
||||||
|
val usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule)
|
||||||
|
val receiverInfo = resolvedCall?.let { getIdForImplicitReceiver(it.dispatchReceiver, simpleNameExpression) }
|
||||||
|
|
||||||
|
combineInfo(receiverInfo, createInfo(declarationDescriptor,
|
||||||
|
variableKind(declarationDescriptor, usageModuleDescriptor,
|
||||||
|
bindingContext, simpleNameExpression)))
|
||||||
|
}
|
||||||
|
is PackageViewDescriptor, is ClassDescriptor -> createPackageOrClassInfo(declarationDescriptor)
|
||||||
|
else -> NO_IDENTIFIER_INFO
|
||||||
}
|
}
|
||||||
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
|
||||||
return createStableInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue());
|
|
||||||
}
|
|
||||||
return NO_IDENTIFIER_INFO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun getIdForImplicitReceiver(receiverValue: ReceiverValue?, expression: KtExpression?) =
|
||||||
private static DeclarationDescriptor getVariableContainingDeclaration(@NotNull VariableDescriptor variableDescriptor) {
|
when (receiverValue) {
|
||||||
DeclarationDescriptor containingDeclarationDescriptor = variableDescriptor.getContainingDeclaration();
|
is ImplicitReceiver -> getIdForThisReceiver(receiverValue.declarationDescriptor)
|
||||||
if (containingDeclarationDescriptor instanceof ConstructorDescriptor
|
is TransientReceiver ->
|
||||||
&& ((ConstructorDescriptor) containingDeclarationDescriptor).isPrimary()) {
|
throw AssertionError("Transient receiver is implicit for an explicit expression: $expression. Receiver: $receiverValue")
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIdForThisReceiver(descriptorOfThisReceiver: DeclarationDescriptor?) = when (descriptorOfThisReceiver) {
|
||||||
|
is CallableDescriptor -> {
|
||||||
|
val receiverParameter = descriptorOfThisReceiver.extensionReceiverParameter
|
||||||
|
?: error("'This' refers to the callable member without a receiver parameter: $descriptorOfThisReceiver")
|
||||||
|
createStableInfo(receiverParameter.value)
|
||||||
|
}
|
||||||
|
is ClassDescriptor -> createStableInfo(descriptorOfThisReceiver.thisAsReceiverParameter.value)
|
||||||
|
else -> NO_IDENTIFIER_INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getVariableContainingDeclaration(variableDescriptor: VariableDescriptor): DeclarationDescriptor {
|
||||||
|
val containingDeclarationDescriptor = variableDescriptor.containingDeclaration
|
||||||
|
return if (containingDeclarationDescriptor is ConstructorDescriptor && containingDeclarationDescriptor.isPrimary) {
|
||||||
// This code is necessary just because JetClassInitializer has no associated descriptor in trace
|
// 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,
|
// Because of it we have to use class itself instead of initializer,
|
||||||
// otherwise we could not find this descriptor inside isAccessedInsideClosure below
|
// otherwise we could not find this descriptor inside isAccessedInsideClosure below
|
||||||
containingDeclarationDescriptor = containingDeclarationDescriptor.getContainingDeclaration();
|
containingDeclarationDescriptor.containingDeclaration
|
||||||
assert containingDeclarationDescriptor != null : "No containing declaration for primary constructor";
|
}
|
||||||
|
else {
|
||||||
|
containingDeclarationDescriptor
|
||||||
}
|
}
|
||||||
return containingDeclarationDescriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAccessedInsideClosure(
|
private fun isAccessedInsideClosure(
|
||||||
@NotNull DeclarationDescriptor variableContainingDeclaration,
|
variableContainingDeclaration: DeclarationDescriptor,
|
||||||
@NotNull BindingContext bindingContext,
|
bindingContext: BindingContext,
|
||||||
@NotNull KtElement accessElement
|
accessElement: KtElement
|
||||||
) {
|
): Boolean {
|
||||||
KtDeclaration parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement);
|
val parent = ControlFlowInformationProvider.getElementParentDeclaration(accessElement)
|
||||||
if (parent != null) {
|
return if (parent != null)
|
||||||
DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent);
|
|
||||||
// Access is at the same declaration: not in closure, lower: in closure
|
// Access is at the same declaration: not in closure, lower: in closure
|
||||||
return !variableContainingDeclaration.equals(descriptor);
|
variableContainingDeclaration != bindingContext.get(DECLARATION_TO_DESCRIPTOR, parent)
|
||||||
}
|
else
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAccessedBeforeAllClosureWriters(
|
private fun isAccessedBeforeAllClosureWriters(
|
||||||
@NotNull DeclarationDescriptor variableContainingDeclaration,
|
variableContainingDeclaration: DeclarationDescriptor,
|
||||||
@NotNull Set<KtDeclaration> writers,
|
writers: Set<KtDeclaration?>,
|
||||||
@NotNull BindingContext bindingContext,
|
bindingContext: BindingContext,
|
||||||
@NotNull KtElement accessElement
|
accessElement: KtElement
|
||||||
) {
|
): Boolean {
|
||||||
// All writers should be before access element, with the exception:
|
// All writers should be before access element, with the exception:
|
||||||
// writer which is the same with declaration site does not count
|
// writer which is the same with declaration site does not count
|
||||||
for (KtDeclaration writer : writers) {
|
writers.filterNotNull().forEach { writer ->
|
||||||
DeclarationDescriptor writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer);
|
val writerDescriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, writer)
|
||||||
// Access is after some writer
|
// Access is after some writer
|
||||||
if (!variableContainingDeclaration.equals(writerDescriptor) && !PsiUtilsKt.before(accessElement, writer)) {
|
if (variableContainingDeclaration != writerDescriptor && !accessElement.before(writer)) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Access is before all writers
|
// Access is before all writers
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Kind propertyKind(@NotNull PropertyDescriptor propertyDescriptor, @Nullable ModuleDescriptor usageModule) {
|
private fun propertyKind(propertyDescriptor: PropertyDescriptor, usageModule: ModuleDescriptor?): Kind {
|
||||||
if (propertyDescriptor.isVar()) return MUTABLE_PROPERTY;
|
if (propertyDescriptor.isVar) return MUTABLE_PROPERTY
|
||||||
if (ModalityKt.isOverridable(propertyDescriptor)) return PROPERTY_WITH_GETTER;
|
if (propertyDescriptor.isOverridable) return PROPERTY_WITH_GETTER
|
||||||
if (!hasDefaultGetter(propertyDescriptor)) return PROPERTY_WITH_GETTER;
|
if (!hasDefaultGetter(propertyDescriptor)) return PROPERTY_WITH_GETTER
|
||||||
if (!invisibleFromOtherModules(propertyDescriptor)) {
|
if (!invisibleFromOtherModules(propertyDescriptor)) {
|
||||||
ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor);
|
val declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor)
|
||||||
if (usageModule == null || !usageModule.equals(declarationModule)) {
|
if (usageModule == null || usageModule != declarationModule) {
|
||||||
return ALIEN_PUBLIC_PROPERTY;
|
return ALIEN_PUBLIC_PROPERTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return STABLE_VALUE;
|
return STABLE_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Kind variableKind(
|
private fun variableKind(
|
||||||
@NotNull VariableDescriptor variableDescriptor,
|
variableDescriptor: VariableDescriptor,
|
||||||
@Nullable ModuleDescriptor usageModule,
|
usageModule: ModuleDescriptor?,
|
||||||
@NotNull BindingContext bindingContext,
|
bindingContext: BindingContext,
|
||||||
@NotNull KtElement accessElement
|
accessElement: KtElement
|
||||||
) {
|
): Kind {
|
||||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
if (variableDescriptor is PropertyDescriptor) {
|
||||||
return propertyKind((PropertyDescriptor) variableDescriptor, usageModule);
|
return propertyKind(variableDescriptor, usageModule)
|
||||||
}
|
}
|
||||||
if (!(variableDescriptor instanceof LocalVariableDescriptor) && !(variableDescriptor instanceof ParameterDescriptor)) return OTHER;
|
if (variableDescriptor !is LocalVariableDescriptor && variableDescriptor !is ParameterDescriptor) return OTHER
|
||||||
if (!variableDescriptor.isVar()) return STABLE_VALUE;
|
if (!variableDescriptor.isVar) return STABLE_VALUE
|
||||||
if (variableDescriptor instanceof SyntheticFieldDescriptor) return MUTABLE_PROPERTY;
|
if (variableDescriptor is SyntheticFieldDescriptor) return MUTABLE_PROPERTY
|
||||||
|
|
||||||
// Local variable classification: PREDICTABLE or UNPREDICTABLE
|
// Local variable classification: PREDICTABLE or UNPREDICTABLE
|
||||||
PreliminaryDeclarationVisitor preliminaryVisitor =
|
val preliminaryVisitor = PreliminaryDeclarationVisitor.getVisitorByVariable(variableDescriptor, bindingContext)
|
||||||
PreliminaryDeclarationVisitor.Companion.getVisitorByVariable(variableDescriptor, bindingContext);
|
?: return UNPREDICTABLE_VARIABLE
|
||||||
// A case when we just analyse an expression alone: counts as unpredictable
|
// A case when we just analyse an expression alone: counts as unpredictable
|
||||||
if (preliminaryVisitor == null) return UNPREDICTABLE_VARIABLE;
|
|
||||||
|
|
||||||
// Analyze who writes variable
|
// Analyze who writes variable
|
||||||
// If there is no writer: predictable
|
// If there is no writer: predictable
|
||||||
Set<KtDeclaration> writers = preliminaryVisitor.writers(variableDescriptor);
|
val writers = preliminaryVisitor.writers(variableDescriptor)
|
||||||
if (writers.isEmpty()) return PREDICTABLE_VARIABLE;
|
if (writers.isEmpty()) return PREDICTABLE_VARIABLE
|
||||||
|
|
||||||
// If access element is inside closure: unpredictable
|
// If access element is inside closure: unpredictable
|
||||||
DeclarationDescriptor variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor);
|
val variableContainingDeclaration = getVariableContainingDeclaration(variableDescriptor)
|
||||||
if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE;
|
if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) return UNPREDICTABLE_VARIABLE
|
||||||
|
|
||||||
// Otherwise, predictable iff considered position is BEFORE all writers except declarer itself
|
// Otherwise, predictable iff considered position is BEFORE all writers except declarer itself
|
||||||
if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement)) return PREDICTABLE_VARIABLE;
|
return if (isAccessedBeforeAllClosureWriters(variableContainingDeclaration, writers, bindingContext, accessElement))
|
||||||
else return UNPREDICTABLE_VARIABLE;
|
PREDICTABLE_VARIABLE
|
||||||
|
else
|
||||||
|
UNPREDICTABLE_VARIABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether a variable with a given descriptor is stable or not at the given usage place.
|
* Determines whether a variable with a given descriptor is stable or not at the given usage place.
|
||||||
* <p/>
|
*
|
||||||
|
*
|
||||||
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
|
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
|
||||||
* <p/>
|
*
|
||||||
|
*
|
||||||
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
|
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
|
||||||
* the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
|
* the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
|
||||||
* (it's private OR internal OR used at the same module where it's defined).
|
* (it's private OR internal OR used at the same module where it's defined).
|
||||||
* The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
|
* The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
|
||||||
*
|
|
||||||
* @param variableDescriptor descriptor of a considered variable
|
* @param variableDescriptor descriptor of a considered variable
|
||||||
|
* *
|
||||||
* @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
|
* @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
|
* @return true if variable is stable, false otherwise
|
||||||
*/
|
*/
|
||||||
public static boolean isStableValue(
|
fun isStableValue(
|
||||||
@NotNull VariableDescriptor variableDescriptor,
|
variableDescriptor: VariableDescriptor,
|
||||||
@Nullable ModuleDescriptor usageModule
|
usageModule: ModuleDescriptor?
|
||||||
) {
|
): Boolean {
|
||||||
if (variableDescriptor.isVar()) return false;
|
if (variableDescriptor.isVar) return false
|
||||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
return variableDescriptor !is PropertyDescriptor || propertyKind(variableDescriptor, usageModule) === STABLE_VALUE
|
||||||
return propertyKind((PropertyDescriptor) variableDescriptor, usageModule) == STABLE_VALUE;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) {
|
private fun invisibleFromOtherModules(descriptor: DeclarationDescriptorWithVisibility): Boolean {
|
||||||
if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true;
|
if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.visibility)) return true
|
||||||
|
|
||||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
val containingDeclaration = descriptor.containingDeclaration
|
||||||
if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) {
|
return containingDeclaration is DeclarationDescriptorWithVisibility && invisibleFromOtherModules(containingDeclaration)
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) {
|
private fun hasDefaultGetter(propertyDescriptor: PropertyDescriptor): Boolean {
|
||||||
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
|
val getter = propertyDescriptor.getter
|
||||||
return getter == null || getter.isDefault();
|
return getter == null || getter.isDefault
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ abstract class AssignedVariablesSearcher: KtTreeVisitorVoid() {
|
|||||||
|
|
||||||
private val assignedNames: SetMultimap<Name, KtDeclaration?> = LinkedHashMultimap.create()
|
private val assignedNames: SetMultimap<Name, KtDeclaration?> = LinkedHashMultimap.create()
|
||||||
|
|
||||||
open fun writers(variableDescriptor: VariableDescriptor) = assignedNames[variableDescriptor.name]
|
open fun writers(variableDescriptor: VariableDescriptor): MutableSet<KtDeclaration?> = assignedNames[variableDescriptor.name]
|
||||||
|
|
||||||
fun hasWriters(variableDescriptor: VariableDescriptor) = writers(variableDescriptor).isNotEmpty()
|
fun hasWriters(variableDescriptor: VariableDescriptor) = writers(variableDescriptor).isNotEmpty()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user