diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt index aba17b8f654..b7c5fa4acd6 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt @@ -188,7 +188,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker { doCheckType( expressionType, c.expectedType, - DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()), + DataFlowValueFactory.createDataFlowValue(expression, expressionType, c), c.dataFlowInfo ) { expectedMustNotBeNull, @@ -202,7 +202,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker { val baseExpression = expression.getBaseExpression() val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return doIfNotNull( - DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()), + DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c), c ) { c.trace.report(Errors.UNNECESSARY_NOT_NULL_ASSERTION.on(expression.getOperationReference(), baseExpressionType)) @@ -214,7 +214,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker { val baseExpression = expression.getLeft() val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return doIfNotNull( - DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()), + DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c), c ) { c.trace.report(Errors.USELESS_ELVIS.on(expression.getOperationReference(), baseExpressionType)) @@ -226,7 +226,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker { JetTokens.EXCLEQEQEQ -> { if (expression.getLeft() != null && expression.getRight() != null) { SenselessComparisonChecker.checkSenselessComparisonWithNull( - expression, expression.getLeft()!!, expression.getRight()!!, c.trace, + expression, expression.getLeft()!!, expression.getRight()!!, c, { c.trace.get(BindingContext.EXPRESSION_TYPE, it) }, { value -> @@ -253,7 +253,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker { safeAccess: Boolean, c: CallResolutionContext<*> ) { - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, c.trace.getBindingContext()) + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, c) if (!safeAccess) { doCheckType( receiverArgument.getType(), diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java index dcd2dcada04..083bdfc2daf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java @@ -444,7 +444,7 @@ public class CandidateResolver { JetExpression deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context); if (deparenthesizedArgument == null || type == null) return type; - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context.trace.getBindingContext()); + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context); if (!dataFlowValue.isStableIdentifier()) return type; Set possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue); @@ -564,8 +564,7 @@ public class CandidateResolver { @NotNull ResolutionContext context ) { ExpressionReceiver receiverToCast = new ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType); - Collection variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver( - context.trace.getBindingContext(), context.dataFlowInfo, receiverToCast); + Collection variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(context, receiverToCast); for (JetType possibleType : variants) { if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) { return possibleType; @@ -637,12 +636,12 @@ public class CandidateResolver { BindingContext bindingContext = trace.getBindingContext(); if (!safeAccess && !receiverParameter.getType().isMarkedNullable() && receiverArgumentType.isMarkedNullable()) { - if (!SmartCastUtils.canBeSmartCast(receiverParameter, receiverArgument, bindingContext, context.dataFlowInfo)) { + if (!SmartCastUtils.canBeSmartCast(receiverParameter, receiverArgument, context)) { context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck); return UNSAFE_CALL_ERROR; } } - DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext); + DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext, context.scope.getContainingDeclaration()); if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) { context.tracing.unnecessarySafeCall(trace, receiverArgumentType); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeApproximator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeApproximator.kt index fa6309c16b8..f95f420b617 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeApproximator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeApproximator.kt @@ -44,8 +44,7 @@ public class TypeApproximator : AdditionalTypeChecker { override val presentableText: String get() = StringUtil.trimMiddle(expression.getText(), 50) - private val dataFlowValue = - DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()) + private val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c) } ) if (approximationInfo != null) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java index 03980b75ea5..2b7d8ecd7d1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java @@ -53,7 +53,6 @@ public class DataFlowValue { /** * Stable identifier is a non-literal value that is statically known to be immutable - * @return */ public boolean isStableIdentifier() { return stableIdentifier; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java index 09f31d86ade..f00522254b6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java @@ -24,7 +24,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.scopes.receivers.*; import org.jetbrains.kotlin.types.JetType; @@ -32,22 +34,40 @@ import org.jetbrains.kotlin.types.TypeUtils; import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET; +/** + * 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. + */ public class DataFlowValueFactory { - private DataFlowValueFactory() {} + private DataFlowValueFactory() { + } @NotNull public static DataFlowValue createDataFlowValue( @NotNull JetExpression expression, @NotNull JetType type, - @NotNull BindingContext bindingContext + @NotNull ResolutionContext resolutionContext + ) { + return createDataFlowValue(expression, type, resolutionContext.trace.getBindingContext(), + resolutionContext.scope.getContainingDeclaration()); + } + + @NotNull + public static DataFlowValue createDataFlowValue( + @NotNull JetExpression expression, + @NotNull JetType type, + @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration ) { if (expression instanceof JetConstantExpression) { JetConstantExpression constantExpression = (JetConstantExpression) expression; if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL; } if (type.isError()) return DataFlowValue.ERROR; - if (KotlinBuiltIns.getInstance().getNullableNothingType().equals(type)) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?' - IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext); + if (KotlinBuiltIns.getInstance().getNullableNothingType().equals(type)) { + return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?' + } + IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclaration); return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, type, result.isStable, getImmanentNullability(type)); } @@ -58,7 +78,20 @@ public class DataFlowValueFactory { } @NotNull - public static DataFlowValue createDataFlowValue(@NotNull ReceiverValue receiverValue, @NotNull BindingContext bindingContext) { + public static DataFlowValue createDataFlowValue( + @NotNull ReceiverValue receiverValue, + @NotNull ResolutionContext resolutionContext + ) { + return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(), + resolutionContext.scope.getContainingDeclaration()); + } + + @NotNull + public static DataFlowValue createDataFlowValue( + @NotNull ReceiverValue receiverValue, + @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration + ) { if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) { // SCRIPT: smartcasts data flow JetType type = receiverValue.getType(); @@ -69,7 +102,10 @@ public class DataFlowValueFactory { return createDataFlowValue((ThisReceiver) receiverValue); } else if (receiverValue instanceof ExpressionReceiver) { - return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(), receiverValue.getType(), bindingContext); + return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(), + receiverValue.getType(), + bindingContext, + containingDeclaration); } else if (receiverValue == ReceiverValue.NO_RECEIVER) { throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER"); @@ -80,9 +116,14 @@ public class DataFlowValueFactory { } @NotNull - public static DataFlowValue createDataFlowValue(@NotNull VariableDescriptor variableDescriptor) { + public static DataFlowValue createDataFlowValue( + @NotNull VariableDescriptor variableDescriptor, + @Nullable ModuleDescriptor usageContainingModule + ) { JetType type = variableDescriptor.getType(); - return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type)); + return new DataFlowValue(variableDescriptor, type, + isStableVariable(variableDescriptor, usageContainingModule), + getImmanentNullability(type)); } @NotNull @@ -133,25 +174,26 @@ public class DataFlowValueFactory { @NotNull private static IdentifierInfo getIdForStableIdentifier( @Nullable JetExpression expression, - @NotNull BindingContext bindingContext + @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration ) { if (expression != null) { JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression); if (expression != deparenthesized) { - return getIdForStableIdentifier(deparenthesized, bindingContext); + return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclaration); } } if (expression instanceof JetQualifiedExpression) { JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression; JetExpression receiverExpression = qualifiedExpression.getReceiverExpression(); JetExpression selectorExpression = qualifiedExpression.getSelectorExpression(); - IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext); - IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext); + IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclaration); + IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclaration); return combineInfo(receiverId, selectorId); } if (expression instanceof JetSimpleNameExpression) { - return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext); + return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext, containingDeclaration); } else if (expression instanceof JetThisExpression) { JetThisExpression thisExpression = (JetThisExpression) expression; @@ -168,21 +210,24 @@ public class DataFlowValueFactory { @NotNull private static IdentifierInfo getIdForSimpleNameExpression( @NotNull JetSimpleNameExpression simpleNameExpression, - @NotNull BindingContext bindingContext + @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration ) { DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression); if (declarationDescriptor instanceof VariableDescriptor) { ResolvedCall resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, 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 " + declarationDescriptor; - + // assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for + ModuleDescriptor usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclaration); IdentifierInfo receiverInfo = resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null; VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor; - return combineInfo(receiverInfo, createInfo(variableDescriptor, isStableVariable(variableDescriptor))); + return combineInfo(receiverInfo, createInfo(variableDescriptor, + isStableVariable(variableDescriptor, usageModuleDescriptor))); } if (declarationDescriptor instanceof PackageViewDescriptor) { return createPackageInfo(declarationDescriptor); @@ -207,7 +252,8 @@ public class DataFlowValueFactory { private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) { if (descriptorOfThisReceiver instanceof CallableDescriptor) { ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter(); - assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + descriptorOfThisReceiver; + assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + + descriptorOfThisReceiver; return createInfo(receiverParameter.getValue(), true); } if (descriptorOfThisReceiver instanceof ClassDescriptor) { @@ -216,13 +262,35 @@ public class DataFlowValueFactory { return NO_IDENTIFIER_INFO; } - public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) { + /** + * Determines whether a variable with a given descriptor is stable or not at the given usage place. + *

+ * Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val). + *

+ * 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 + * (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". + * + * @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) + * @return true if variable is stable, false otherwise + */ + public static boolean isStableVariable( + @NotNull VariableDescriptor variableDescriptor, + @Nullable ModuleDescriptor usageModule + ) { if (variableDescriptor.isVar()) return false; if (variableDescriptor instanceof PropertyDescriptor) { PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; - if (!invisibleFromOtherModules(propertyDescriptor)) return false; if (!isFinal(propertyDescriptor)) return false; if (!hasDefaultGetter(propertyDescriptor)) return false; + if (!invisibleFromOtherModules(propertyDescriptor)) { + ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor); + if (usageModule == null || !usageModule.equals(declarationModule)) { + return false; + } + } } return true; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java index f076eb4c000..927893a6d5c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java @@ -22,6 +22,7 @@ import kotlin.Function1; import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.resolve.BindingContext; @@ -53,18 +54,19 @@ public class SmartCastUtils { @NotNull ReceiverValue receiverToCast, @NotNull ResolutionContext context ) { - return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.dataFlowInfo); + return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getContainingDeclaration(), context.dataFlowInfo); } @NotNull public static List getSmartCastVariants( @NotNull ReceiverValue receiverToCast, @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration, @NotNull DataFlowInfo dataFlowInfo ) { List variants = Lists.newArrayList(); variants.add(receiverToCast.getType()); - variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, dataFlowInfo, receiverToCast)); + variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclaration, dataFlowInfo, receiverToCast)); return variants; } @@ -72,9 +74,11 @@ public class SmartCastUtils { public static List getSmartCastVariantsWithLessSpecificExcluded( @NotNull ReceiverValue receiverToCast, @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration, @NotNull DataFlowInfo dataFlowInfo ) { - final List variants = getSmartCastVariants(receiverToCast, bindingContext, dataFlowInfo); + final List variants = getSmartCastVariants(receiverToCast, bindingContext, + containingDeclaration, dataFlowInfo); return KotlinPackage.filter(variants, new Function1() { @Override public Boolean invoke(final JetType type) { @@ -88,12 +92,27 @@ public class SmartCastUtils { }); } + /** + * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included + */ + @NotNull + public static Collection getSmartCastVariantsExcludingReceiver( + @NotNull ResolutionContext context, + @NotNull ReceiverValue receiverToCast + ) { + return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(), + context.scope.getContainingDeclaration(), + context.dataFlowInfo, + receiverToCast); + } + /** * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included */ @NotNull public static Collection getSmartCastVariantsExcludingReceiver( @NotNull BindingContext bindingContext, + @NotNull DeclarationDescriptor containingDeclaration, @NotNull DataFlowInfo dataFlowInfo, @NotNull ReceiverValue receiverToCast ) { @@ -104,7 +123,8 @@ public class SmartCastUtils { return dataFlowInfo.getPossibleTypes(dataFlowValue); } else if (receiverToCast instanceof ExpressionReceiver) { - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverToCast, bindingContext); + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue( + receiverToCast, bindingContext, containingDeclaration); return dataFlowInfo.getPossibleTypes(dataFlowValue); } return Collections.emptyList(); @@ -152,13 +172,12 @@ public class SmartCastUtils { return false; } - Collection smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver( - context.trace.getBindingContext(), context.dataFlowInfo, receiver); + Collection smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(context, receiver); JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver); if (smartCastSubType == null) return false; JetExpression expression = ((ExpressionReceiver) receiver).getExpression(); - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext()); + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context); recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true); return true; @@ -187,12 +206,10 @@ public class SmartCastUtils { public static boolean canBeSmartCast( @NotNull ReceiverParameterDescriptor receiverParameter, @NotNull ReceiverValue receiver, - @NotNull BindingContext bindingContext, - @NotNull DataFlowInfo dataFlowInfo - ) { + @NotNull ResolutionContext context) { if (!receiver.getType().isMarkedNullable()) return true; - List smartCastVariants = getSmartCastVariants(receiver, bindingContext, dataFlowInfo); + List smartCastVariants = getSmartCastVariants(receiver, context); for (JetType smartCastVariant : smartCastVariants) { if (JetTypeChecker.DEFAULT.isSubtypeOf(smartCastVariant, receiverParameter.getType())) return true; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 9dead9d0906..d8eaaa7b240 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -170,7 +170,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType); dataFlowInfo = typeInfo.getDataFlowInfo(); if (operationType == AS_KEYWORD) { - DataFlowValue value = createDataFlowValue(left, subjectType, context.trace.getBindingContext()); + DataFlowValue value = createDataFlowValue(left, subjectType, context); dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType); } } @@ -224,7 +224,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { return; } Collection possibleTypes = DataFlowUtils.getAllPossibleTypes( - expression.getLeft(), context.dataFlowInfo, actualType, context.trace.getBindingContext()); + expression.getLeft(), context.dataFlowInfo, actualType, context); for (JetType possibleType : possibleTypes) { if (typeChecker.isSubtypeOf(possibleType, targetType)) { context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression)); @@ -890,7 +890,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, baseType)); } else { - DataFlowValue value = createDataFlowValue(baseExpression, baseType, context.trace.getBindingContext()); + DataFlowValue value = createDataFlowValue(baseExpression, baseType, context); dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL); } // The call to checkType() is only needed here to execute additionalTypeCheckers, hence the NO_EXPECTED_TYPE @@ -932,7 +932,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } private static boolean isKnownToBeNotNull(JetExpression expression, JetType jetType, ExpressionTypingContext context) { - DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context.trace.getBindingContext()); + DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context); return !context.dataFlowInfo.getNullability(dataFlowValue).canBeNull(); } @@ -1194,7 +1194,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo(); if (leftType != null && rightType != null && KotlinBuiltIns.isNothingOrNullableNothing(rightType) && !rightType.isMarkedNullable()) { - DataFlowValue value = createDataFlowValue(left, leftType, context.trace.getBindingContext()); + DataFlowValue value = createDataFlowValue(left, leftType, context); dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL); } JetType type = resolvedCall.getResultingDescriptor().getReturnType(); @@ -1262,7 +1262,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType)); } SenselessComparisonChecker.checkSenselessComparisonWithNull( - expression, left, right, context.trace, + expression, left, right, context, new Function1() { @Override public JetType invoke(JetExpression expression) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java index 5f09778bb5c..7d2cab0b3d3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java @@ -53,7 +53,11 @@ public class DataFlowUtils { } @NotNull - public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) { + public static DataFlowInfo extractDataFlowInfoFromCondition( + @Nullable JetExpression condition, + final boolean conditionValue, + final ExpressionTypingContext context + ) { if (condition == null) return context.dataFlowInfo; final Ref result = new Ref(null); condition.accept(new JetVisitorVoid() { @@ -93,9 +97,8 @@ public class DataFlowUtils { JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); if (rhsType == null) return; - BindingContext bindingContext = context.trace.getBindingContext(); - DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, bindingContext); - DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, bindingContext); + DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, context); + DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, context); Boolean equals = null; if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { @@ -192,7 +195,7 @@ public class DataFlowUtils { return expressionType; } - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()); + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c); for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) { if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) { @@ -254,9 +257,9 @@ public class DataFlowUtils { @NotNull JetExpression expression, @NotNull DataFlowInfo dataFlowInfo, @NotNull JetType type, - @NotNull BindingContext bindingContext + @NotNull ResolutionContext c ) { - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, bindingContext); + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c); Collection possibleTypes = Sets.newHashSet(type); if (dataFlowValue.isStableIdentifier()) { possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index 935fdf623dd..710aa07870c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.AnnotationResolver; +import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.ModifiersChecker; import org.jetbrains.kotlin.resolve.TemporaryBindingTrace; import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache; @@ -141,8 +142,9 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito dataFlowInfo = typeInfo.getDataFlowInfo(); JetType type = typeInfo.getType(); if (property.getTypeReference() == null && type != null) { - DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue(propertyDescriptor); - DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context.trace.getBindingContext()); + DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue( + propertyDescriptor, DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration())); + DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context); dataFlowInfo = dataFlowInfo.equate(variableDataFlowValue, initializerDataFlowValue); } } @@ -341,8 +343,8 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito dataFlowInfo = rightInfo.getDataFlowInfo(); JetType rightType = rightInfo.getType(); if (left != null && leftType != null && rightType != null) { - DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context.trace.getBindingContext()); - DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context.trace.getBindingContext()); + DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context); + DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context); dataFlowInfo = dataFlowInfo.equate(leftValue, rightValue); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java index be07d741e90..e46728049ed 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.java @@ -56,8 +56,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { JetType knownType = typeInfo.getType(); DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo(); if (expression.getTypeReference() != null) { - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, - context.trace.getBindingContext()); + DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context); DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo; DataFlowInfo newDataFlowInfo = conditionInfo.and(dataFlowInfo); context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo); @@ -87,7 +86,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo()); } DataFlowValue subjectDataFlowValue = subjectExpression != null - ? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context.trace.getBindingContext()) + ? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context) : DataFlowValue.NULL; // TODO : exhaustive patterns @@ -263,7 +262,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { } checkTypeCompatibility(context, type, subjectType, expression); DataFlowValue expressionDataFlowValue = - DataFlowValueFactory.createDataFlowValue(expression, type, context.trace.getBindingContext()); + DataFlowValueFactory.createDataFlowValue(expression, type, context); DataFlowInfos result = noChange(context); result = new DataFlowInfos( result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue), diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt index bd801b79dd2..2d24bac4849 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/SenselessComparisonChecker.kt @@ -25,13 +25,14 @@ import org.jetbrains.kotlin.diagnostics.Errors import kotlin.platform.platformStatic import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext object SenselessComparisonChecker { platformStatic fun checkSenselessComparisonWithNull( expression: JetBinaryExpression, left: JetExpression, right: JetExpression, - trace: BindingTrace, + context: ResolutionContext<*>, getType: (JetExpression) -> JetType?, getNullability: (DataFlowValue) -> Nullability ) { @@ -44,7 +45,7 @@ object SenselessComparisonChecker { if (type == null || type.isError()) return val operationSign = expression.getOperationReference() - val value = DataFlowValueFactory.createDataFlowValue(expr, type, trace.getBindingContext()) + val value = DataFlowValueFactory.createDataFlowValue(expr, type, context) val equality = operationSign.getReferencedNameElementType() == JetTokens.EQEQ || operationSign.getReferencedNameElementType() == JetTokens.EQEQEQ val nullability = getNullability(value) @@ -55,6 +56,6 @@ object SenselessComparisonChecker { else if (nullability == Nullability.IMPOSSIBLE) false else return - trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways)) + context.trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways)) } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt362.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt362.kt index ed902a31568..d97799f8a87 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt362.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/kt362.kt @@ -5,8 +5,8 @@ package example fun test() { val p = test.Public() - if (p.public is Int) p.public + 1 - if (p.protected is Int) p.protected + 1 + if (p.public is Int) p.public + 1 + if (p.protected is Int) p.protected + 1 if (p.internal is Int) p.internal + 1 val i = test.Internal() if (i.public is Int) i.public + 1 diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVal.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVal.kt index 519afc38522..d1e1267658f 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVal.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVal.kt @@ -1,10 +1,11 @@ -public class A() { - public val foo: Int? = 1 +public open class A() { + public open val foo: Int? = 1 } fun Int.bar(i: Int) = i fun test() { val p = A() + // For open value properties, smart casts should not work if (p.foo is Int) p.foo bar 11 } diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVal.txt b/compiler/testData/diagnostics/tests/smartCasts/publicVal.txt index ec6d380dcaf..007ff90c8a2 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVal.txt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVal.txt @@ -3,9 +3,9 @@ package internal fun test(): kotlin.Unit internal fun kotlin.Int.bar(/*0*/ i: kotlin.Int): kotlin.Int -public final class A { +public open class A { public constructor A() - public final val foo: kotlin.Int? = 1 + public open val foo: kotlin.Int? = 1 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 diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.kt new file mode 100644 index 00000000000..0379d2af41c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.kt @@ -0,0 +1,13 @@ +public trait A { + public val x: Any +} + +public class B(override public val x: Any) : A { + fun foo(): Int { + if (x is String) { + return x.length() + } else { + return 0 + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.txt new file mode 100644 index 00000000000..0fb3ec02cb5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.txt @@ -0,0 +1,17 @@ +package + +public trait A { + public abstract val x: kotlin.Any + 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 B : A { + public constructor B(/*0*/ x: kotlin.Any) + public open override /*1*/ val x: kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.kt new file mode 100644 index 00000000000..74d84052059 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.kt @@ -0,0 +1,12 @@ +public class X { + public val x : String? = null + public fun fn(): Int { + if (x != null) + // Smartcast is possible because it's value property with default getter + // used in the same module + return x.length() + else + return 0 + } +} + diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.txt new file mode 100644 index 00000000000..bddd58f2c62 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.txt @@ -0,0 +1,10 @@ +package + +public final class X { + public constructor X() + public final val x: kotlin.String? = null + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun fn(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.kt new file mode 100644 index 00000000000..2ced3f89cc8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.kt @@ -0,0 +1,13 @@ +public class X { + private val x : String? = null + public val y: CharSequence? + get() = x?.subSequence(0, 1) + public fun fn(): Int { + if (y != null) + // With non-default getter smartcast is not possible + return y.length() + else + return 0 + } +} + diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.txt new file mode 100644 index 00000000000..f8827de37ff --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.txt @@ -0,0 +1,11 @@ +package + +public final class X { + public constructor X() + private final val x: kotlin.String? = null + public final val y: kotlin.CharSequence? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun fn(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.kt new file mode 100644 index 00000000000..ec06fed34e7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.kt @@ -0,0 +1,22 @@ +class Delegate { + fun get(thisRef: Any?, prop: PropertyMetadata): String? { + return null + } +} + +class Example { + private val p: String? by Delegate() + + public val r: String? = "xyz" + + public fun foo(): String { + // Smart cast is not possible if property is delegated + return if (p != null) p else "" + } + + public fun bar(): String { + // But is possible for non-delegated value property even if it's public + return if (r != null) r else "" + } +} + diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.txt new file mode 100644 index 00000000000..782741e3dc4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.txt @@ -0,0 +1,20 @@ +package + +internal final class Delegate { + public constructor Delegate() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun get(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.PropertyMetadata): kotlin.String? + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +internal final class Example { + public constructor Example() + private final val p: kotlin.String? + public final val r: kotlin.String? = "xyz" + public final fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.kt new file mode 100644 index 00000000000..7b4520f88ae --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.kt @@ -0,0 +1,35 @@ +// MODULE: m1 +// FILE: a.kt + +package a + +public class X { + public val x : String? = null +} + +// MODULE: m2(m1) +// FILE: b.kt + +package b + +import a.X + +public fun X.gav(): Int { + if (x != null) + // Smart cast is not possible if definition is in another module + return x.length() + else + return 0 +} + +// FILE: c.kt + +package a + +public fun X.gav(): Int { + if (x != null) + // Even if it's in the same package + return x.length() + else + return 0 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.txt new file mode 100644 index 00000000000..30882c2c4b0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.txt @@ -0,0 +1,29 @@ +// -- Module: -- +package + +package a { + + public final class X { + public constructor X() + public final val x: kotlin.String? = null + 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 + } +} + + +// -- Module: -- +package + +package a { + public fun a.X.gav(): kotlin.Int + + public final class X { + // -- Module: -- + } +} + +package b { + public fun a.X.gav(): kotlin.Int +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.kt new file mode 100644 index 00000000000..08ac5719d4f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.kt @@ -0,0 +1,17 @@ +public open class X { + protected val x : String? = null + public fun fn(): Int { + if (x != null) + // Smartcast is possible for protected value property in the same class + return x.length() + else + return 0 + } +} + +public class Y: X() { + public fun bar(): Int { + // Smartcast is possible even in derived class + return if (x != null) x.length() else 0 + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.txt new file mode 100644 index 00000000000..8884e887072 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.txt @@ -0,0 +1,20 @@ +package + +public open class X { + public constructor X() + protected final val x: kotlin.String? = null + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun fn(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Y : X { + public constructor Y() + protected final override /*1*/ /*fake_override*/ val x: kotlin.String? + public final fun bar(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun fn(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.kt new file mode 100644 index 00000000000..7b12ff2b7af --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.kt @@ -0,0 +1,15 @@ +public class X { + public var x : String? = null + private var y: String? = "abc" + public fun fn(): Int { + if (x != null) + // Smartcast is not possible for variable properties + return x.length() + else if (y != null) + // Even if they are private + return y.length() + else + return 0 + } +} + diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.txt new file mode 100644 index 00000000000..32cc51e460b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.txt @@ -0,0 +1,11 @@ +package + +public final class X { + public constructor X() + public final var x: kotlin.String? + private final var y: kotlin.String? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun fn(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 2759483403b..7a0c5f78191 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -11018,6 +11018,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { @TestDataPath("$PROJECT_ROOT") @InnerTestClasses({ SmartCasts.Inference.class, + SmartCasts.Varnotnull.class, }) @RunWith(JUnit3RunnerWithInners.class) public static class SmartCasts extends AbstractJetDiagnosticsTest { @@ -11177,6 +11178,57 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } } + + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Varnotnull extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInVarnotnull() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/varnotnull"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("kt4409.kt") + public void testKt4409() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.kt"); + doTest(fileName); + } + + @TestMetadata("kt5907.kt") + public void testKt5907() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.kt"); + doTest(fileName); + } + + @TestMetadata("kt5907customGetter.kt") + public void testKt5907customGetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.kt"); + doTest(fileName); + } + + @TestMetadata("kt5907delegate.kt") + public void testKt5907delegate() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.kt"); + doTest(fileName); + } + + @TestMetadata("kt5907otherModule.kt") + public void testKt5907otherModule() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.kt"); + doTest(fileName); + } + + @TestMetadata("kt5907protected.kt") + public void testKt5907protected() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.kt"); + doTest(fileName); + } + + @TestMetadata("kt5907var.kt") + public void testKt5907var() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/substitutions") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 4cfe801f943..23f8650725c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -182,12 +182,17 @@ public class DescriptorUtils { @NotNull public static ModuleDescriptor getContainingModule(@NotNull DeclarationDescriptor descriptor) { + ModuleDescriptor module = getContainingModuleOrNull(descriptor); + assert module != null : "Descriptor without a containing module: " + descriptor; + return module; + } + + @Nullable + public static ModuleDescriptor getContainingModuleOrNull(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof PackageViewDescriptor) { return ((PackageViewDescriptor) descriptor).getModule(); } - ModuleDescriptor module = getParentOfType(descriptor, ModuleDescriptor.class, false); - assert module != null : "Descriptor without a containing module: " + descriptor; - return module; + return getParentOfType(descriptor, ModuleDescriptor.class, false); } @Nullable diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt index 5be3c38c4f8..9621c8f3264 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt @@ -100,7 +100,10 @@ public class ReferenceVariantsHelper( val receiverValue = ExpressionReceiver(receiverExpression, expressionType) val dataFlowInfo = context.getDataFlowInfo(expression) - for (variant in SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, context, dataFlowInfo)) { + for (variant in SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, + context, + resolutionScope.getContainingDeclaration(), + dataFlowInfo)) { descriptors.addMembersFromReceiver(variant, callType, kindFilter, nameFilter) } diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt index 5a92e2ecd5e..e816cb4016e 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/extensionsUtils.kt @@ -48,12 +48,12 @@ public fun CallableDescriptor.substituteExtensionIfCallable( dataFlowInfo: DataFlowInfo, callType: CallType ): Collection { - val stream = receivers.stream().flatMap { substituteExtensionIfCallable(it, callType, context, dataFlowInfo).stream() } + val sequence = receivers.sequence().flatMap { substituteExtensionIfCallable(it, callType, context, dataFlowInfo).sequence() } if (getTypeParameters().isEmpty()) { // optimization for non-generic callables - return stream.firstOrNull()?.let { listOf(it) } ?: listOf() + return sequence.firstOrNull()?.let { listOf(it) } ?: listOf() } else { - return stream.toList() + return sequence.toList() } } @@ -69,7 +69,7 @@ public fun CallableDescriptor.substituteExtensionIfCallable( if (!receiver.exists()) return listOf() if (!callType.canCall(this)) return listOf() - var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo).stream() + var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, getContainingDeclaration(), dataFlowInfo).sequence() if (callType == CallType.SAFE) { types = types.map { it.makeNotNullable() } diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 4f4afafa928..fc5d7791066 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -137,7 +137,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess val (receivers, callType) = referenceVariantsHelper!!.getReferenceVariantsReceivers(expression) val dataFlowInfo = bindingContext!!.getDataFlowInfo(expression) var receiverTypes = receivers.flatMap { - SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it, bindingContext, dataFlowInfo) + SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it, bindingContext, moduleDescriptor, dataFlowInfo) } if (callType == CallType.SAFE) { receiverTypes = receiverTypes.map { it.makeNotNullable() } diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index 63d793448d1..7958317b835 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -131,7 +131,8 @@ class SmartCompletion( else filteredExpectedInfos - val smartCastTypes: (VariableDescriptor) -> Collection = SmartCastCalculator(bindingContext).calculate(expressionWithType, receiver) + val smartCastTypes: (VariableDescriptor) -> Collection = SmartCastCalculator( + bindingContext, moduleDescriptor).calculate(expressionWithType, receiver) val itemsToSkip = calcItemsToSkip(expressionWithType) @@ -419,7 +420,8 @@ class SmartCompletion( tail: Tail?, typeFilter: (FuzzyType) -> Boolean ): Result { - val smartCastTypes: (VariableDescriptor) -> Collection = SmartCastCalculator(bindingContext).calculate(position, receiver) + val smartCastTypes: (VariableDescriptor) -> Collection = SmartCastCalculator( + bindingContext, moduleDescriptor).calculate(position, receiver) fun filterDeclaration(descriptor: DeclarationDescriptor): Collection { val types = descriptor.fuzzyTypes(smartCastTypes) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt index 3b16c746068..e16e457b238 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt @@ -16,34 +16,21 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.psi.JetBlockExpression -import org.jetbrains.kotlin.psi.JetBinaryExpression -import org.jetbrains.kotlin.psi.JetIfExpression -import org.jetbrains.kotlin.psi.JetPsiUtil import org.jetbrains.kotlin.lexer.JetTokens -import org.jetbrains.kotlin.psi.JetPsiFactory import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler -import org.jetbrains.kotlin.psi.JetSafeQualifiedExpression import org.jetbrains.kotlin.resolve.BindingContext import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler -import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.descriptors.VariableDescriptor import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.LocalSearchScope -import org.jetbrains.kotlin.psi.JetDeclaration -import org.jetbrains.kotlin.psi.JetThrowExpression -import org.jetbrains.kotlin.psi.JetPostfixExpression -import org.jetbrains.kotlin.psi.JetCallExpression import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement -import org.jetbrains.kotlin.psi.JetProperty import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.psi.* val NULL_PTR_EXCEPTION = "NullPointerException" val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException" @@ -65,9 +52,9 @@ fun JetExpression.extractExpressionIfSingle(): JetExpression? { val innerExpression = JetPsiUtil.deparenthesize(this) if (innerExpression is JetBlockExpression) { return if (innerExpression.getStatements().size() == 1) - JetPsiUtil.deparenthesize(innerExpression.getStatements().first as? JetExpression) - else - null + JetPsiUtil.deparenthesize(innerExpression.getStatements().firstOrNull() as? JetExpression) + else + null } return innerExpression @@ -86,7 +73,7 @@ fun JetBinaryExpression.getNonNullExpression(): JetExpression? = when { fun JetExpression.isNullExpression(): Boolean = this.extractExpressionIfSingle()?.getText() == "null" -fun JetExpression.isNullExpressionOrEmptyBlock(): Boolean = this.isNullExpression() || this is JetBlockExpression && this.getStatements().empty +fun JetExpression.isNullExpressionOrEmptyBlock(): Boolean = this.isNullExpression() || this is JetBlockExpression && this.getStatements().isEmpty() fun JetExpression.isThrowExpression(): Boolean = this.extractExpressionIfSingle() is JetThrowExpression @@ -171,5 +158,6 @@ fun JetPostfixExpression.inlineBaseExpressionIfApplicableWithPrompt(editor: Edit fun JetExpression.isStableVariable(): Boolean { val context = this.analyze() val descriptor = BindingContextUtils.extractVariableDescriptorIfAny(context, this, false) - return descriptor is VariableDescriptor && DataFlowValueFactory.isStableVariable(descriptor) + return descriptor is VariableDescriptor && + DataFlowValueFactory.isStableVariable(descriptor, DescriptorUtils.getContainingModule(descriptor)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index 1f05b7ea316..f8dd34fef89 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -94,7 +94,7 @@ fun JetType.getTypeParameters(): Set { fun JetExpression.guessTypes( context: BindingContext, - module: ModuleDescriptor?, + module: ModuleDescriptor, coerceUnusedToUnit: Boolean = true ): Array { val builtIns = KotlinBuiltIns.getInstance() @@ -108,7 +108,7 @@ fun JetExpression.guessTypes( val theType1 = context[BindingContext.EXPRESSION_TYPE, this] if (theType1 != null) { val dataFlowInfo = context[BindingContext.EXPRESSION_DATA_FLOW_INFO, this] - val possibleTypes = dataFlowInfo?.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context)) + val possibleTypes = dataFlowInfo?.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context, module)) return if (possibleTypes != null && possibleTypes.isNotEmpty()) possibleTypes.copyToArray() else array(theType1) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt index 98d0ad4955a..7b414e98949 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.types.TypeProjectionImpl import java.util.Collections import org.jetbrains.kotlin.types.JetTypeImpl @@ -40,9 +41,9 @@ object CreateIteratorFunctionActionFactory : JetIntentionActionsFactory() { val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE) val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType() - val context = file.analyzeFully() - val returnJetTypeParameterTypes = variableExpr.guessTypes(context, null) - if (returnJetTypeParameterTypes.size != 1) return null + val analysisResult = file.analyzeFullyAndGetResult() + val returnJetTypeParameterTypes = variableExpr.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor) + if (returnJetTypeParameterTypes.size() != 1) return null val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0]) val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType) diff --git a/idea/src/org/jetbrains/kotlin/idea/util/SmartCastCalculator.kt b/idea/src/org/jetbrains/kotlin/idea/util/SmartCastCalculator.kt index 6f6b9f00a27..204531c2d4d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/SmartCastCalculator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/SmartCastCalculator.kt @@ -32,8 +32,10 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import java.util.HashMap import java.util.HashSet import com.intellij.openapi.util.Pair +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext -class SmartCastCalculator(val bindingContext: BindingContext) { +class SmartCastCalculator(val bindingContext: BindingContext, val containingDeclaration: DeclarationDescriptor) { public fun calculate(position: JetExpression, receiver: JetExpression?): (VariableDescriptor) -> Collection { val dataFlowInfo = bindingContext.getDataFlowInfo(position) val (variableToTypes, notNullVariables) = processDataFlowInfo(dataFlowInfo, receiver) @@ -63,14 +65,14 @@ class SmartCastCalculator(val bindingContext: BindingContext) { val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor? if (receiver != null) { val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver] ?: return ProcessDataFlowInfoResult() - val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId() - dataFlowValueToVariable = {(value) -> + val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext, containingDeclaration).getId() + dataFlowValueToVariable = { value -> val id = value.getId() if (id is Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null } } else { - dataFlowValueToVariable = {(value) -> + dataFlowValueToVariable = { value -> val id = value.getId() when { id is VariableDescriptor -> id diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt index eaa6473a129..1a29952b1a1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/AbstractJetExtractionTest.kt @@ -161,7 +161,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe } } ) - handler.selectElements(editor, file) {(elements, previousSibling) -> + handler.selectElements(editor, file) { elements, previousSibling -> handler.doInvoke(editor, file, elements, explicitPreviousSibling ?: previousSibling) } } @@ -177,7 +177,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe val mainFileName = mainFile.getName() val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName) mainFile.getParentFile() - .listFiles {(file, name) -> + .listFiles { file, name -> name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java")) }.forEach { fixture.configureByFile(it.getName()) @@ -192,7 +192,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe try { action(file) - assert(!conflictFile.exists()) + assert(!conflictFile.exists(), "Conflict file $conflictFile should not exist") JetTestUtils.assertEqualsToFile(afterFile, file.getText()!!) } catch(e: Exception) {