Move and expand hack about not-null smart casts for type parameters

This commit is contained in:
Denis Zharkov
2015-08-27 16:19:48 +03:00
parent acf6491eed
commit 6ed6b2e298
3 changed files with 74 additions and 28 deletions
@@ -198,6 +198,56 @@ public class SmartCastManager {
return canBeCast;
}
@Nullable
public SmartCastResult checkAndRecordPossibleCast(
@NotNull DataFlowValue dataFlowValue,
@NotNull JetType expectedType,
@Nullable JetExpression expression,
@NotNull ResolutionContext c,
boolean recordExpressionType
) {
for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) {
if (expression != null) {
recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isPredictable(), recordExpressionType);
}
return new SmartCastResult(possibleType, dataFlowValue.isPredictable());
}
}
if (!c.dataFlowInfo.getNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable()) {
// Handling cases like:
// fun bar(x: Any) {}
// fun <T : Any?> foo(x: T) {
// if (x != null) {
// bar(x) // Should be allowed with smart cast
// }
// }
//
// It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
// But at the same time we're sure that `x` can't be null and just check for such cases manually
// E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds
// x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression)
boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull();
JetType nullableExpectedType = TypeUtils.makeNullable(expectedType);
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType)) {
if (!immanentlyNotNull) {
if (expression != null) {
recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue.isPredictable(),
recordExpressionType);
}
}
return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable());
}
return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, recordExpressionType);
}
return null;
}
public boolean recordSmartCastToNotNullIfPossible(
@NotNull ReceiverValue receiver,
@NotNull ResolutionContext context
@@ -0,0 +1,21 @@
/*
* 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.resolve.calls.smartcasts
import org.jetbrains.kotlin.types.JetType
public class SmartCastResult(public val resultType: JetType, public val isCorrect: Boolean)
@@ -28,10 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager;
import org.jetbrains.kotlin.resolve.calls.smartcasts.*;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.types.JetType;
@@ -219,30 +216,8 @@ public class DataFlowAnalyzer {
) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
smartCastManager.recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isPredictable(), false);
return possibleType;
}
}
// For cases like:
// fun bar(x: Any) {}
// fun <T : Any?> foo(x: T) {
// if (x != null) {
// bar(x) // Should be allowed with smart cast
// }
// }
//
// It doesn't handled by upper code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
// But at the same time we're sure that `x` can't be null and just check for such cases manually
if (!c.dataFlowInfo.getNullability(dataFlowValue).canBeNull()
&& JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, TypeUtils.makeNullable(c.expectedType))) {
smartCastManager.recordCastOrError(expression, expressionType, c.trace, dataFlowValue.isPredictable(), false);
return expressionType;
}
return null;
SmartCastResult result = smartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, false);
return result != null ? result.getResultType() : null;
}
public void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {