From 2d3ce89afc74a017310850e5439f19b10b49ace6 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 6 Jun 2017 02:36:48 +0300 Subject: [PATCH] Use only completed arguments of special call --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 ++ .../rendering/DefaultErrorMessages.java | 2 ++ .../resolve/calls/ArgumentTypeResolver.java | 14 ++++++++- .../BasicExpressionTypingVisitor.java | 21 ++++++++++---- .../ControlStructureTypingVisitor.java | 29 ++++++++++++++++++- .../callableReference/generic/specialCalls.kt | 2 +- .../controlFlowAnalysis/elvisNotProcessed.kt | 4 +-- .../lambdasInExclExclAndElvis.kt | 2 +- 8 files changed, 64 insertions(+), 12 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index b955a1e277c..c563c092b8d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -792,9 +792,11 @@ public interface Errors { DiagnosticFactory0 UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING); DiagnosticFactory0 NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE = DiagnosticFactory0.create(WARNING); DiagnosticFactory1 USELESS_ELVIS = DiagnosticFactory1.create(WARNING, PositioningStrategies.USELESS_ELVIS); DiagnosticFactory0 USELESS_ELVIS_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 USELESS_ELVIS_ON_CALLABLE_REFERENCE = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 USELESS_ELVIS_RIGHT_IS_NULL = DiagnosticFactory0.create(WARNING, PositioningStrategies.USELESS_ELVIS); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 8e0011cd379..20a10fbd340 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -479,6 +479,7 @@ public class DefaultErrorMessages { MAP.put(DYNAMIC_UPPER_BOUND, "Dynamic type can not be used as an upper bound"); MAP.put(USELESS_ELVIS, "Elvis operator (?:) always returns the left operand of non-nullable type {0}", RENDER_TYPE); MAP.put(USELESS_ELVIS_ON_LAMBDA_EXPRESSION, "Left operand of elvis operator (?:) is a lambda expression"); + MAP.put(USELESS_ELVIS_ON_CALLABLE_REFERENCE, "Left operand of elvis operator (?:) is a callable reference expression"); MAP.put(USELESS_ELVIS_RIGHT_IS_NULL, "Right operand of elvis operator (?:) is useless if it is null"); MAP.put(CONFLICTING_UPPER_BOUNDS, "Upper bounds of {0} have empty intersection", NAME); @@ -583,6 +584,7 @@ public class DefaultErrorMessages { MAP.put(UNEXPECTED_SAFE_CALL, "Safe-call is not allowed here"); MAP.put(UNNECESSARY_NOT_NULL_ASSERTION, "Unnecessary non-null assertion (!!) on a non-null receiver of type {0}", RENDER_TYPE); MAP.put(NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION, "Non-null assertion (!!) is called on a lambda expression"); + MAP.put(NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE, "Non-null assertion (!!) is called on a callable reference expression"); MAP.put(NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER, "{0} does not refer to a type parameter of {1}", (typeConstraint, context) -> { //noinspection ConstantConditions return typeConstraint.getSubjectTypeParameterName().getReferencedName(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java index 98ee7708319..d86ece87f85 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/ArgumentTypeResolver.java @@ -162,6 +162,18 @@ public class ArgumentTypeResolver { return getFunctionLiteralArgumentIfAny(expression, context) != null; } + public static boolean isCallableReferenceArgument( + @NotNull KtExpression expression, @NotNull ResolutionContext context + ) { + return getCallableReferenceExpressionIfAny(expression, context) != null; + } + + public static boolean isFunctionLiteralOrCallableReference( + @NotNull KtExpression expression, @NotNull ResolutionContext context + ) { + return isFunctionLiteralArgument(expression, context) || isCallableReferenceArgument(expression, context); + } + @NotNull public static KtFunction getFunctionLiteralArgument( @NotNull KtExpression expression, @NotNull ResolutionContext context @@ -188,7 +200,7 @@ public class ArgumentTypeResolver { @Nullable public static KtCallableReferenceExpression getCallableReferenceExpressionIfAny( @NotNull KtExpression expression, - @NotNull CallResolutionContext context + @NotNull ResolutionContext context ) { KtExpression deparenthesizedExpression = getLastElementDeparenthesized(expression, context.statementFilter); if (deparenthesizedExpression instanceof KtCallableReferenceExpression) { 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 a29c38538cc..d184841322d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.config.LanguageVersionSettings; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.Diagnostic; +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; @@ -826,8 +827,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { call, ResolveConstruct.EXCL_EXCL, Collections.singletonList("baseExpr"), Collections.singletonList(true), context, null); KotlinTypeInfo baseTypeInfo = BindingContextUtils.getRecordedTypeInfo(baseExpression, context.trace.getBindingContext()); - if (ArgumentTypeResolver.isFunctionLiteralArgument(baseExpression, context)) { - context.trace.report(NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION.on(operationSign)); + boolean isFunctionLiteral = ArgumentTypeResolver.isFunctionLiteralArgument(baseExpression, context); + boolean isCallableReference = ArgumentTypeResolver.isCallableReferenceArgument(baseExpression, context); + if (isFunctionLiteral || isCallableReference) { + DiagnosticFactory0 diagnosticFactory = + isFunctionLiteral ? NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION : NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE; + context.trace.report(diagnosticFactory.on(operationSign)); if (baseTypeInfo == null) { return TypeInfoFactoryKt.createTypeInfo(ErrorUtils.createErrorType("Unresolved lambda expression"), context); } @@ -1218,9 +1223,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { call, ResolveConstruct.ELVIS, Lists.newArrayList("left", "right"), Lists.newArrayList(true, false), contextWithExpectedType, null); KotlinTypeInfo leftTypeInfo = BindingContextUtils.getRecordedTypeInfo(left, context.trace.getBindingContext()); - if (ArgumentTypeResolver.isFunctionLiteralArgument(left, context)) { - context.trace.report(USELESS_ELVIS_ON_LAMBDA_EXPRESSION.on(expression.getOperationReference())); - if (leftTypeInfo == null) return TypeInfoFactoryKt.noTypeInfo(context); + boolean isLeftFunctionLiteral = ArgumentTypeResolver.isFunctionLiteralArgument(left, context); + boolean isLeftCallableReference = ArgumentTypeResolver.isCallableReferenceArgument(left, context); + if (leftTypeInfo == null && (isLeftFunctionLiteral || isLeftCallableReference)) { + DiagnosticFactory0 diagnosticFactory = + isLeftFunctionLiteral ? USELESS_ELVIS_ON_LAMBDA_EXPRESSION : USELESS_ELVIS_ON_CALLABLE_REFERENCE; + context.trace.report(diagnosticFactory.on(expression.getOperationReference())); + return TypeInfoFactoryKt.noTypeInfo(context); } assert leftTypeInfo != null : "Left expression was not processed: " + expression; KotlinType leftType = leftTypeInfo.getType(); @@ -1231,7 +1240,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.report(USELESS_ELVIS_RIGHT_IS_NULL.on(expression)); } KotlinTypeInfo rightTypeInfo = BindingContextUtils.getRecordedTypeInfo(right, context.trace.getBindingContext()); - if (rightTypeInfo == null && ArgumentTypeResolver.isFunctionLiteralArgument(right, context)) { + if (rightTypeInfo == null && ArgumentTypeResolver.isFunctionLiteralOrCallableReference(right, context)) { // the type is computed later in call completer according to the '?:' semantics as a function return TypeInfoFactoryKt.noTypeInfo(context); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 303910e330b..c177f81c682 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.ModifierCheckerCore; import org.jetbrains.kotlin.resolve.ModifiersChecker; +import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; @@ -132,10 +133,35 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { Lists.newArrayList(false, false), contextWithExpectedType, dataFlowInfoForArguments); + return processBranches( + ifExpression, contextWithExpectedType, context, conditionDataFlowInfo, + loopBreakContinuePossibleInCondition, elseBranch, thenBranch, resolvedCall); + } + + @NotNull + private KotlinTypeInfo processBranches( + KtIfExpression ifExpression, + ExpressionTypingContext contextWithExpectedType, + ExpressionTypingContext context, + DataFlowInfo conditionDataFlowInfo, + boolean loopBreakContinuePossibleInCondition, + KtExpression elseBranch, + KtExpression thenBranch, + ResolvedCall resolvedCall + ) { BindingContext bindingContext = context.trace.getBindingContext(); KotlinTypeInfo thenTypeInfo = BindingContextUtils.getRecordedTypeInfo(thenBranch, bindingContext); KotlinTypeInfo elseTypeInfo = BindingContextUtils.getRecordedTypeInfo(elseBranch, bindingContext); - assert thenTypeInfo != null || elseTypeInfo != null : "Both branches of if expression were not processed: " + ifExpression.getText(); + + boolean isThenPostponed = ArgumentTypeResolver.isFunctionLiteralOrCallableReference(thenBranch, context); + boolean isElsePostponed = ArgumentTypeResolver.isFunctionLiteralOrCallableReference(thenBranch, context); + + assert thenTypeInfo != null || elseTypeInfo != null || + isThenPostponed || isElsePostponed : "Both branches of if expression were not processed: " + ifExpression.getText(); + + if (thenTypeInfo == null && elseTypeInfo == null) { + return TypeInfoFactoryKt.noTypeInfo(context); + } KotlinType resultType = resolvedCall.getResultingDescriptor().getReturnType(); boolean loopBreakContinuePossible = loopBreakContinuePossibleInCondition; @@ -184,6 +210,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { return TypeInfoFactoryKt.noTypeInfo(resultDataFlowInfo); } } + // If break or continue was possible, take condition check info as the jump info return TypeInfoFactoryKt.createTypeInfo( components.dataFlowAnalyzer.checkType(resultType, ifExpression, contextWithExpectedType), diff --git a/compiler/testData/diagnostics/tests/callableReference/generic/specialCalls.kt b/compiler/testData/diagnostics/tests/callableReference/generic/specialCalls.kt index 2b20a44c4ff..aee3d92a0ac 100644 --- a/compiler/testData/diagnostics/tests/callableReference/generic/specialCalls.kt +++ b/compiler/testData/diagnostics/tests/callableReference/generic/specialCalls.kt @@ -17,7 +17,7 @@ fun test() { else -> ::baz }) - val x5: (Int) -> Int = bar(::baz!!) + val x5: (Int) -> Int = bar(::baz!!) (if (true) ::baz else ::baz)(1) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt index 7af302ef80c..c1fe575449b 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt @@ -1,6 +1,6 @@ // See KT-8277 -val v = { true } ?: ( { true } ?: null!! ) +val v = { true } ?: ( { true } ?: null!! ) val w = if (true) { { true } @@ -32,6 +32,6 @@ val bbb = null ?: ( l() ?: null) val bbbb = ( l() ?: null) ?: ( l() ?: null) fun f(x : Long?): Long { - var a = x ?: (fun() {} ?: fun() {}) + var a = x ?: (fun() {} ?: fun() {}) return a } diff --git a/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt b/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt index fbf5455390e..08ff52dfa6a 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt @@ -7,7 +7,7 @@ fun test() { use({ }!!); // KT-KT-9070 - { } ?: 1 + { } ?: 1 use({ 2 } ?: 1); 1 ?: { }