Use only completed arguments of special call

This commit is contained in:
Mikhail Zarechenskiy
2017-06-06 02:36:48 +03:00
parent a687dea898
commit 2d3ce89afc
8 changed files with 64 additions and 12 deletions
@@ -792,9 +792,11 @@ public interface Errors {
DiagnosticFactory0<PsiElement> UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<PsiElement> NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<KtBinaryExpression, KotlinType> USELESS_ELVIS = DiagnosticFactory1.create(WARNING, PositioningStrategies.USELESS_ELVIS);
DiagnosticFactory0<PsiElement> USELESS_ELVIS_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> USELESS_ELVIS_ON_CALLABLE_REFERENCE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtBinaryExpression> USELESS_ELVIS_RIGHT_IS_NULL =
DiagnosticFactory0.create(WARNING, PositioningStrategies.USELESS_ELVIS);
@@ -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();
@@ -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) {
@@ -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<PsiElement> 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<PsiElement> 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);
}
@@ -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<FunctionDescriptor> 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),
@@ -17,7 +17,7 @@ fun test() {
else -> ::baz
})
val x5: (Int) -> Int = bar(::baz<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>)
val x5: (Int) -> Int = bar(::baz<!NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE!>!!<!>)
(if (true) ::baz else ::baz)(1)
}
@@ -1,6 +1,6 @@
// See KT-8277
val v = { true } <!USELESS_ELVIS!><!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> ( { true } <!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> null!! )<!>
val v = { true } <!USELESS_ELVIS!>?: ( { true } <!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> null!! )<!>
val w = if (true) {
{ true }
@@ -32,6 +32,6 @@ val bbb = null ?: ( l() <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>)
val bbbb = ( l() <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>) ?: ( l() <!USELESS_ELVIS_RIGHT_IS_NULL!>?: null<!>)
fun f(x : Long?): Long {
var a = x ?: (<!TYPE_MISMATCH!>fun() {}<!> <!USELESS_ELVIS!><!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> <!TYPE_MISMATCH!>fun() {}<!><!>)
var a = x ?: (<!TYPE_MISMATCH!>fun() {}<!> <!USELESS_ELVIS!>?: <!TYPE_MISMATCH!>fun() {}<!><!>)
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!>
}
@@ -7,7 +7,7 @@ fun test() {
use({ }<!NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION!>!!<!>);
// KT-KT-9070
<!TYPE_MISMATCH!>{ }<!> <!USELESS_ELVIS!><!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> 1<!>
<!TYPE_MISMATCH!>{ }<!> <!USELESS_ELVIS!>?: 1<!>
use({ 2 } <!USELESS_ELVIS_ON_LAMBDA_EXPRESSION!>?:<!> 1);
1 <!USELESS_ELVIS!>?: <!TYPE_MISMATCH, UNUSED_LAMBDA_EXPRESSION!>{ }<!><!>