Deprecate smartcasts on local delegated properties

^KT-22517 Fixed
This commit is contained in:
Dmitry Savvinov
2018-06-21 12:11:47 +03:00
parent 31ce992ff2
commit 70714cb71e
16 changed files with 129 additions and 17 deletions
@@ -903,6 +903,7 @@ public interface Errors {
DiagnosticFactory2<KtExpression, KotlinType, KotlinType> IMPLICIT_CAST_TO_ANY = DiagnosticFactory2.create(WARNING);
DiagnosticFactory3<KtExpression, KotlinType, String, String> SMARTCAST_IMPOSSIBLE = DiagnosticFactory3.create(ERROR);
DiagnosticFactory3<KtExpression, KotlinType, String, String> DEPRECATED_SMARTCAST = DiagnosticFactory3.create(WARNING);
DiagnosticFactory0<KtExpression> ALWAYS_NULL = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtNullableType> USELESS_NULLABLE_CHECK = DiagnosticFactory0.create(WARNING, NULLABLE_TYPE);
@@ -622,6 +622,8 @@ public class DefaultErrorMessages {
}, DECLARATION_NAME);
MAP.put(SMARTCAST_IMPOSSIBLE,
"Smart cast to ''{0}'' is impossible, because ''{1}'' is a {2}", RENDER_TYPE, STRING, STRING);
MAP.put(DEPRECATED_SMARTCAST,
"Smart cast to ''{0}'' is deprecated, because ''{1}'' is a {2}", RENDER_TYPE, STRING, STRING);
MAP.put(ALWAYS_NULL, "The result of the expression is always null");
MAP.put(MISSING_CONSTRUCTOR_KEYWORD, "Use 'constructor' keyword after modifiers of primary constructor");
@@ -43,6 +43,8 @@ class DataFlowValue(
STABLE_VALUE("stable val"),
// Block, or if / else, or when
STABLE_COMPLEX_EXPRESSION("complex expression", ""),
// Should be unstable, but can be used as stable with deprecation warning
LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY("local delegated property"),
// Member value with open / custom getter
// Smart casts are not safe
PROPERTY_WITH_GETTER("custom getter", "property that has open or custom getter"),
@@ -69,7 +71,10 @@ class DataFlowValue(
* Stable means here we do not expect some sudden change of their values,
* like accessing mutable properties in another thread, so smart casts can be used safely.
*/
val isStable = (kind == Kind.STABLE_VALUE || kind == Kind.STABLE_VARIABLE || kind == Kind.STABLE_COMPLEX_EXPRESSION)
val isStable = kind == Kind.STABLE_VALUE ||
kind == Kind.STABLE_VARIABLE ||
kind == Kind.STABLE_COMPLEX_EXPRESSION ||
kind == Kind.LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY
val canBeBound get() = identifierInfo.canBeBound
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
@@ -24,8 +25,8 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.isError
class DataFlowValueFactoryImpl
@Deprecated("Please, avoid to use that implementation explicitly. If you need DataFlowValueFactory, use injection") constructor() :
DataFlowValueFactory {
@Deprecated("Please, avoid to use that implementation explicitly. If you need DataFlowValueFactory, use injection")
constructor(private val languageVersionSettings: LanguageVersionSettings) : DataFlowValueFactory {
// Receivers
override fun createDataFlowValue(
@@ -61,7 +62,7 @@ class DataFlowValueFactoryImpl
): DataFlowValue {
val identifierInfo = IdentifierInfo.Variable(
variableDescriptor,
variableDescriptor.variableKind(usageContainingModule, bindingContext, property),
variableDescriptor.variableKind(usageContainingModule, bindingContext, property, languageVersionSettings),
bindingContext[BindingContext.BOUND_INITIALIZER_VALUE, variableDescriptor]
)
return DataFlowValue(identifierInfo, variableDescriptor.type)
@@ -102,7 +103,7 @@ class DataFlowValueFactoryImpl
DataFlowValue(IdentifierInfo.Expression(expression, stableComplex = true), type)
else -> {
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule)
val result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule, languageVersionSettings)
DataFlowValue(if (result === IdentifierInfo.NO) IdentifierInfo.Expression(expression) else result, type)
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
@@ -33,11 +34,22 @@ internal fun PropertyDescriptor.propertyKind(usageModule: ModuleDescriptor?): Da
internal fun VariableDescriptor.variableKind(
usageModule: ModuleDescriptor?,
bindingContext: BindingContext,
accessElement: KtElement
accessElement: KtElement,
languageVersionSettings: LanguageVersionSettings
): DataFlowValue.Kind {
if (this is PropertyDescriptor) {
return propertyKind(usageModule)
}
if (this is LocalVariableDescriptor && this.isDelegated) {
// Local delegated property: normally unstable, but can be treated as stable in legacy mode
return if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSmartcastsOnLocalDelegatedProperty))
DataFlowValue.Kind.PROPERTY_WITH_GETTER
else
DataFlowValue.Kind.LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY
}
if (this !is LocalVariableDescriptor && this !is ParameterDescriptor) return DataFlowValue.Kind.OTHER
if (!isVar) return DataFlowValue.Kind.STABLE_VALUE
if (this is SyntheticFieldDescriptor) return DataFlowValue.Kind.MUTABLE_PROPERTY
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve.calls.smartcasts
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens
@@ -124,20 +125,21 @@ interface IdentifierInfo {
internal fun getIdForStableIdentifier(
expression: KtExpression?,
bindingContext: BindingContext,
containingDeclarationOrModule: DeclarationDescriptor
containingDeclarationOrModule: DeclarationDescriptor,
languageVersionSettings: LanguageVersionSettings
): IdentifierInfo {
if (expression != null) {
val deparenthesized = KtPsiUtil.deparenthesize(expression)
if (expression !== deparenthesized) {
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule)
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule, languageVersionSettings)
}
}
return when (expression) {
is KtQualifiedExpression -> {
val receiverExpression = expression.receiverExpression
val selectorExpression = expression.selectorExpression
val receiverInfo = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule)
val selectorInfo = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule)
val receiverInfo = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule, languageVersionSettings)
val selectorInfo = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule, languageVersionSettings)
qualified(
receiverInfo, bindingContext.getType(receiverExpression),
@@ -153,7 +155,7 @@ internal fun getIdForStableIdentifier(
IdentifierInfo.NO
} else {
IdentifierInfo.SafeCast(
getIdForStableIdentifier(subjectExpression, bindingContext, containingDeclarationOrModule),
getIdForStableIdentifier(subjectExpression, bindingContext, containingDeclarationOrModule, languageVersionSettings),
bindingContext.getType(subjectExpression),
bindingContext[BindingContext.TYPE, targetTypeReference]
)
@@ -161,7 +163,7 @@ internal fun getIdForStableIdentifier(
}
is KtSimpleNameExpression ->
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule)
getIdForSimpleNameExpression(expression, bindingContext, containingDeclarationOrModule, languageVersionSettings)
is KtThisExpression -> {
val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
@@ -171,7 +173,15 @@ internal fun getIdForStableIdentifier(
is KtPostfixExpression -> {
val operationType = expression.operationReference.getReferencedNameElementType()
if (operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS)
postfix(getIdForStableIdentifier(expression.baseExpression, bindingContext, containingDeclarationOrModule), operationType)
postfix(
getIdForStableIdentifier(
expression.baseExpression,
bindingContext,
containingDeclarationOrModule,
languageVersionSettings
),
operationType
)
else
IdentifierInfo.NO
}
@@ -183,7 +193,8 @@ internal fun getIdForStableIdentifier(
private fun getIdForSimpleNameExpression(
simpleNameExpression: KtSimpleNameExpression,
bindingContext: BindingContext,
containingDeclarationOrModule: DeclarationDescriptor
containingDeclarationOrModule: DeclarationDescriptor,
languageVersionSettings: LanguageVersionSettings
): IdentifierInfo {
val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, simpleNameExpression)
return when (declarationDescriptor) {
@@ -197,7 +208,7 @@ private fun getIdForSimpleNameExpression(
val usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule)
val selectorInfo = IdentifierInfo.Variable(
declarationDescriptor,
declarationDescriptor.variableKind(usageModuleDescriptor, bindingContext, simpleNameExpression),
declarationDescriptor.variableKind(usageModuleDescriptor, bindingContext, simpleNameExpression, languageVersionSettings),
bindingContext[BindingContext.BOUND_INITIALIZER_VALUE, declarationDescriptor]
)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtExpression
@@ -133,6 +134,10 @@ class SmartCastManager {
) {
if (KotlinBuiltIns.isNullableNothing(type)) return
if (dataFlowValue.isStable) {
if (dataFlowValue.kind == DataFlowValue.Kind.LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY) {
trace.report(Errors.DEPRECATED_SMARTCAST.on(expression, type, expression.text, dataFlowValue.kind.description))
}
val oldSmartCasts = trace[SMARTCAST, expression]
val newSmartCast = SingleSmartCast(call, type)
if (oldSmartCasts != null) {