From e9693e987f9224d58df1c6196a8f85138d26938f Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 15 Jan 2016 12:22:44 +0300 Subject: [PATCH] Humanize type mismatch diagnostic caused by type projections #KT-10581 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 6 + .../kotlin/diagnostics/diagnosticUtils.kt | 115 ++++++++++++++++++ .../rendering/DefaultErrorMessages.java | 17 +++ .../kotlin/resolve/calls/CallCompleter.kt | 19 +-- .../kotlin/resolve/calls/CandidateResolver.kt | 4 +- .../context/BasicCallResolutionContext.java | 17 +-- .../CallCandidateResolutionContext.java | 15 ++- .../resolve/calls/context/CallPosition.kt | 37 ++++++ .../calls/context/CallResolutionContext.java | 5 +- .../calls/context/ResolutionContext.java | 32 +++-- .../calls/tasks/AbstractTracingStrategy.java | 31 +++-- .../resolve/calls/tasks/ResolutionTask.java | 12 +- .../resolve/calls/tasks/TracingStrategy.java | 19 ++- ...egyForImplicitConstructorDelegationCall.kt | 5 +- .../constants/CompileTimeConstantChecker.java | 34 ++++-- .../BasicExpressionTypingVisitor.java | 4 +- .../ControlStructureTypingUtils.java | 12 +- .../types/expressions/DataFlowAnalyzer.java | 7 +- .../expressions/ExpressionTypingContext.java | 21 ++-- .../expressions/ExpressionTypingUtils.java | 3 +- .../ExpressionTypingVisitorDispatcher.java | 4 +- .../ExpressionTypingVisitorForStatements.java | 7 +- .../kt7383_starProjectedFunction.kt | 2 +- .../diagnostics/tests/generics/Projections.kt | 8 +- .../generics/innerClasses/simpleOutUseSite.kt | 6 +- .../tests/generics/projectionsScope/addAll.kt | 16 ++- .../generics/projectionsScope/addAll.txt | 1 + .../extensionReceiverTypeMismatch.kt | 19 +++ .../extensionReceiverTypeMismatch.txt | 20 +++ .../projectionsScope/inValueParameter.kt | 8 ++ .../projectionsScope/inValueParameter.txt | 16 +++ .../leakedApproximatedType.kt | 19 +++ .../leakedApproximatedType.txt | 26 ++++ .../multipleArgumentProjectedOut.kt | 9 ++ .../multipleArgumentProjectedOut.txt | 11 ++ .../projectedOutConventions.kt | 13 ++ .../projectedOutConventions.txt | 13 ++ .../typeMismatchConventions.kt | 19 +++ .../typeMismatchConventions.txt | 20 +++ .../projectionsScope/typeMismatchInLambda.kt | 26 ++++ .../projectionsScope/typeMismatchInLambda.txt | 21 ++++ .../generics/projectionsScope/varargs.kt | 12 ++ .../generics/projectionsScope/varargs.txt | 11 ++ .../setterNotProjectedOutAssign.kt | 2 +- .../varProjection/setterProjectedOutAssign.kt | 4 +- .../setterProjectedOutNoPlusAssign.kt | 2 +- .../testsWithStdLib/addAllProjection.kt | 10 +- .../checkers/DiagnosticsTestGenerated.java | 48 ++++++++ .../inference/CapturedTypeConstructor.kt | 6 +- .../kotlin/types/CapturedTypeApproximation.kt | 3 +- .../idea/highlighter/IdeErrorMessages.java | 18 +++ .../expectedNothingDueToProjections.kt | 6 + .../expectedNothingDueToProjections1.txt | 2 + .../typeMismatchDueToProjections.kt | 6 + .../typeMismatchDueToProjections1.html | 17 +++ .../typeMismatchDueToProjectionsIn.kt | 11 ++ .../typeMismatchDueToProjectionsIn1.html | 17 +++ .../typeMismatchDueToProjectionsTxt.kt | 7 ++ .../typeMismatchDueToProjectionsTxt1.txt | 2 + .../DiagnosticMessageTestGenerated.java | 24 ++++ 60 files changed, 796 insertions(+), 111 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallPosition.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.txt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.kt create mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.txt create mode 100644 idea/testData/diagnosticMessage/expectedNothingDueToProjections.kt create mode 100644 idea/testData/diagnosticMessage/expectedNothingDueToProjections1.txt create mode 100644 idea/testData/diagnosticMessage/typeMismatchDueToProjections.kt create mode 100644 idea/testData/diagnosticMessage/typeMismatchDueToProjections1.html create mode 100644 idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn.kt create mode 100644 idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn1.html create mode 100644 idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt.kt create mode 100644 idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt1.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index a90bd14ec6a..32f13f94634 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.kotlin.types.TypeProjection; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -714,6 +715,8 @@ public interface Errors { // Type mismatch DiagnosticFactory2 TYPE_MISMATCH = DiagnosticFactory2.create(ERROR); + DiagnosticFactory1 TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS = DiagnosticFactory1.create(ERROR); + DiagnosticFactory2 MEMBER_PROJECTED_OUT = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 RETURN_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 EXPECTED_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 ASSIGNMENT_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR); @@ -785,6 +788,9 @@ public interface Errors { ImmutableSet> MUST_BE_INITIALIZED_DIAGNOSTICS = ImmutableSet.of( MUST_BE_INITIALIZED, MUST_BE_INITIALIZED_OR_BE_ABSTRACT ); + ImmutableSet> TYPE_MISMATCH_ERRORS = ImmutableSet.of( + TYPE_MISMATCH, CONSTANT_EXPECTED_TYPE_MISMATCH, NULL_FOR_NONNULL_TYPE, TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS, + MEMBER_PROJECTED_OUT); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt new file mode 100644 index 00000000000..94e1bac2c8e --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.diagnostics + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.context.CallPosition +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.inference.isCaptured +import org.jetbrains.kotlin.resolve.calls.inference.wrapWithCapturingSubstitution +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeConstructorSubstitution +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny +import org.jetbrains.kotlin.types.typeUtil.isNothing +import org.jetbrains.kotlin.types.typeUtil.isNullableNothing + +fun ResolutionContext<*>.reportTypeMismatchDueToTypeProjection( + expression: KtElement, + expectedType: KotlinType, + expressionType: KotlinType? +): Boolean { + if (!TypeUtils.containsSpecialType(expectedType) { it.isAnyOrNullableAny() || it.isNothing() || it.isNullableNothing() }) return false + + val callPosition = this.callPosition + val (resolvedCall, correspondingNotApproximatedTypeByDescriptor: (CallableDescriptor) -> KotlinType?) = when (callPosition) { + is CallPosition.ValueArgumentPosition -> Pair( + callPosition.resolvedCall, { + f: CallableDescriptor -> + getEffectiveExpectedType(f.valueParameters[callPosition.valueParameter.index], callPosition.valueArgument) + }) + is CallPosition.ExtensionReceiverPosition -> Pair( + callPosition.resolvedCall, { + f: CallableDescriptor -> + f.extensionReceiverParameter?.type + }) + is CallPosition.PropertyAssignment -> Pair( + callPosition.leftPart.getResolvedCall(trace.bindingContext) ?: return false, { + f: CallableDescriptor -> + (f as? PropertyDescriptor)?.setter?.valueParameters?.get(0)?.type + }) + is CallPosition.Unknown -> return false + } + + val receiverType = resolvedCall.dispatchReceiver?.type ?: return false + val callableDescriptor = resolvedCall.resultingDescriptor.original + + val substitutedDescriptor = + TypeConstructorSubstitution + .create(receiverType) + .wrapWithCapturingSubstitution(needApproximation = false) + .buildSubstitutor().let { callableDescriptor.substitute(it) } ?: return false + + val nonApproximatedExpectedType = correspondingNotApproximatedTypeByDescriptor(substitutedDescriptor) ?: return false + if (!TypeUtils.containsSpecialType(nonApproximatedExpectedType) { it.isCaptured() }) return false + + if (expectedType.isNothing()) { + if (callPosition is CallPosition.PropertyAssignment) { + trace.report(Errors.SETTER_PROJECTED_OUT.on(callPosition.leftPart ?: return false, resolvedCall.resultingDescriptor)) + } + else { + val call = resolvedCall.call + val reportOn = + if (resolvedCall is VariableAsFunctionResolvedCall) + resolvedCall.variableCall.call.calleeExpression + else + call.calleeExpression + + trace.reportDiagnosticOnce(Errors.MEMBER_PROJECTED_OUT.on(reportOn ?: call.callElement, callableDescriptor, receiverType)) + } + } + else { + // expressionType can be null when reporting CONSTANT_EXPECTED_TYPE_MISMATCH (see addAll.kt test) + expressionType ?: return false + trace.report( + Errors.TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS.on( + expression, TypeMismatchDueToTypeProjectionsData( + expectedType, expressionType, receiverType, callableDescriptor))) + + } + + return true +} + +private fun BindingTrace.reportDiagnosticOnce(diagnostic: Diagnostic) { + if (bindingContext.diagnostics.forElement(diagnostic.psiElement).any { it.factory == diagnostic.factory }) return + + report(diagnostic) +} + +class TypeMismatchDueToTypeProjectionsData( + val expectedType: KotlinType, + val expressionType: KotlinType, + val receiverType: KotlinType, + val callableDescriptor: CallableDescriptor +) 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 d9e3004f874..d2e85ec66a4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.TestOnly; import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory; import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.diagnostics.TypeMismatchDueToTypeProjectionsData; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.psi.KtTypeConstraint; @@ -126,6 +127,22 @@ public class DefaultErrorMessages { MAP.put(ACCESSOR_PARAMETER_NAME_SHADOWING, "Accessor parameter name 'field' is shadowed by backing field variable"); MAP.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE); + MAP.put(TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS, + "Type mismatch: inferred type is {1} but {0} was expected. Projected type {2} restricts use of {3}", + new MultiRenderer() { + @NotNull + @Override + public String[] render(@NotNull TypeMismatchDueToTypeProjectionsData object) { + return new String[] { + RENDER_TYPE.render(object.getExpectedType()), + RENDER_TYPE.render(object.getExpressionType()), + RENDER_TYPE.render(object.getReceiverType()), + DescriptorRenderer.FQ_NAMES_IN_TYPES.render(object.getCallableDescriptor()) + }; + } + }); + + MAP.put(MEMBER_PROJECTED_OUT, "Out-projected type ''{1}'' prohibits the use of ''{0}''", DescriptorRenderer.FQ_NAMES_IN_TYPES, RENDER_TYPE); MAP.put(INCOMPATIBLE_MODIFIERS, "Modifier ''{0}'' is incompatible with ''{1}''", TO_STRING, TO_STRING); MAP.put(DEPRECATED_MODIFIER_PAIR, "Modifier ''{0}'' is deprecated in presence of ''{1}''", TO_STRING, TO_STRING); MAP.put(REPEATED_MODIFIER, "Repeated ''{0}''", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index 8dd7656fcba..602612bdf0c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariabl import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.CallPosition import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData @@ -68,7 +69,6 @@ class CallCompleter( // for the case 'foo(a)' where 'foo' is a variable, the call 'foo.invoke(a)' shouldn't be completed separately, // it's completed when the outer (variable as function call) is completed if (!isInvokeCallOnVariable(context.call)) { - val temporaryTrace = TemporaryBindingTrace.create(context.trace, "Trace to complete a resulting call") completeResolvedCallAndArguments(resolvedCall, results, context.replaceBindingTrace(temporaryTrace), tracing) @@ -222,7 +222,7 @@ class CallCompleter( candidateDescriptor, constraintSystem!!, valueArgumentsCheckingResult.argumentTypes, receiverType, context.expectedType, context.call ) - tracing.typeInferenceFailed(context.trace, errorData) + tracing.typeInferenceFailed(context, errorData) addStatus(ResolutionStatus.OTHER_ERROR) } @@ -247,11 +247,16 @@ class CallCompleter( for (valueArgument in context.call.valueArguments) { val argumentMapping = getArgumentMapping(valueArgument!!) - val expectedType = when (argumentMapping) { - is ArgumentMatch -> getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument) - else -> TypeUtils.NO_EXPECTED_TYPE + val (expectedType, callPosition) = when (argumentMapping) { + is ArgumentMatch -> Pair( + getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument), + CallPosition.ValueArgumentPosition(results.resultingCall, argumentMapping.valueParameter, valueArgument)) + else -> Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown) } - val newContext = context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument)).replaceExpectedType(expectedType) + val newContext = + context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument)) + .replaceExpectedType(expectedType) + .replaceCallPosition(callPosition) completeOneArgument(valueArgument, newContext) } } @@ -306,7 +311,7 @@ class CallCompleter( val (cachedResolutionResults, cachedContext, tracing) = cachedData val contextForArgument = cachedContext.replaceBindingTrace(context.trace) - .replaceExpectedType(context.expectedType).replaceCollectAllCandidates(false) + .replaceExpectedType(context.expectedType).replaceCollectAllCandidates(false).replaceCallPosition(context.callPosition) return completeCall(contextForArgument, cachedResolutionResults, tracing) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index 007563f12fa..4f58f8a7d30 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -449,7 +449,9 @@ class CandidateResolver( receiverArgument, receiverParameter.type, this) if (!isSubtypeBySmartCastIgnoringNullability) { - tracing.wrongReceiverType(trace, receiverParameter, receiverArgument) + tracing.wrongReceiverType( + trace, receiverParameter, receiverArgument, + this.replaceCallPosition(CallPosition.ExtensionReceiverPosition(candidateCall))) return OTHER_ERROR } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java index 0a18b23802e..b6b57a59e78 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/BasicCallResolutionContext.java @@ -42,10 +42,11 @@ public class BasicCallResolutionContext extends CallResolutionContext resolvedCall) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallCandidateResolutionContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallCandidateResolutionContext.java index 053f008e97d..1de0adc386e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallCandidateResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallCandidateResolutionContext.java @@ -58,11 +58,12 @@ public final class CallCandidateResolutionContext @Nullable Receiver explicitExtensionReceiverForInvoke, @NotNull CandidateResolveMode candidateResolveMode, boolean isAnnotationContext, - boolean collectAllCandidates + boolean collectAllCandidates, + @NotNull CallPosition callPosition ) { super(trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, dataFlowInfoForArguments, callChecker, statementFilter, isAnnotationContext, - collectAllCandidates); + collectAllCandidates, callPosition); this.candidateCall = candidateCall; this.tracing = tracing; this.explicitExtensionReceiverForInvoke = explicitExtensionReceiverForInvoke; @@ -79,7 +80,7 @@ public final class CallCandidateResolutionContext context.dataFlowInfo, context.contextDependency, context.checkArguments, context.resolutionResultsCache, context.dataFlowInfoForArguments, context.callChecker, context.statementFilter, explicitExtensionReceiverForInvoke, - candidateResolveMode, context.isAnnotationContext, context.collectAllCandidates); + candidateResolveMode, context.isAnnotationContext, context.collectAllCandidates, context.callPosition); } @NotNull @@ -90,7 +91,8 @@ public final class CallCandidateResolutionContext candidateCall, tracing, context.trace, context.scope, context.call, context.expectedType, context.dataFlowInfo, context.contextDependency, context.checkArguments, context.resolutionResultsCache, context.dataFlowInfoForArguments, context.callChecker, context.statementFilter, - null, CandidateResolveMode.FULLY, context.isAnnotationContext, context.collectAllCandidates); + null, CandidateResolveMode.FULLY, context.isAnnotationContext, context.collectAllCandidates, + context.callPosition); } @Override @@ -102,11 +104,12 @@ public final class CallCandidateResolutionContext @NotNull ContextDependency contextDependency, @NotNull ResolutionResultsCache resolutionResultsCache, @NotNull StatementFilter statementFilter, - boolean collectAllCandidates + boolean collectAllCandidates, + @NotNull CallPosition callPosition ) { return new CallCandidateResolutionContext( candidateCall, tracing, trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, dataFlowInfoForArguments, callChecker, statementFilter, - explicitExtensionReceiverForInvoke, candidateResolveMode, isAnnotationContext, collectAllCandidates); + explicitExtensionReceiverForInvoke, candidateResolveMode, isAnnotationContext, collectAllCandidates, callPosition); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallPosition.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallPosition.kt new file mode 100644 index 00000000000..2d6b86074cb --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallPosition.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.context + +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + + +sealed class CallPosition { + object Unknown : CallPosition() + + class ExtensionReceiverPosition(val resolvedCall: ResolvedCall<*>) : CallPosition() + + class ValueArgumentPosition( + val resolvedCall: ResolvedCall<*>, + val valueParameter: ValueParameterDescriptor, + val valueArgument: ValueArgument + ) : CallPosition() + + class PropertyAssignment(val leftPart: KtExpression?) : CallPosition() +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java index 2107d52a501..3e095d57cfa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/context/CallResolutionContext.java @@ -50,10 +50,11 @@ public abstract class CallResolutionContext c + ) { + KtExpression reportOn = receiverArgument instanceof ExpressionReceiver + ? ((ExpressionReceiver) receiverArgument).getExpression() + : reference; + + if (!DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection( + c, reportOn, receiverParameter.getType(), receiverArgument.getType())) { + trace.report(TYPE_MISMATCH.on(reportOn, receiverParameter.getType(), receiverArgument.getType())); } } @@ -195,7 +202,7 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { } @Override - public void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull InferenceErrorData data) { + public void typeInferenceFailed(@NotNull ResolutionContext context, @NotNull InferenceErrorData data) { ConstraintSystem constraintSystem = data.constraintSystem; ConstraintSystemStatus status = constraintSystem.getStatus(); assert !status.isSuccessful() : "Report error only for not successful constraint system"; @@ -205,6 +212,7 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { // (it's useful, when the arguments, e.g. lambdas or calls are incomplete) return; } + BindingTrace trace = context.trace; if (status.hasOnlyErrorsDerivedFrom(EXPECTED_TYPE_POSITION)) { KotlinType declaredReturnType = data.descriptor.getReturnType(); if (declaredReturnType == null) return; @@ -215,7 +223,10 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { assert substitutedReturnType != null; //todo assert !noExpectedType(data.expectedType) : "Expected type doesn't exist, but there is an expected type mismatch error"; - trace.report(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.on(call.getCallElement(), data.expectedType, substitutedReturnType)); + if (!DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection( + context, call.getCallElement(), data.expectedType, substitutedReturnType)) { + trace.report(TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.on(call.getCallElement(), data.expectedType, substitutedReturnType)); + } } else if (status.hasCannotCaptureTypesError()) { trace.report(TYPE_INFERENCE_CANNOT_CAPTURE_TYPES.on(reference, data)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTask.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTask.java index ec2546ab1c7..b1aecb2576f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTask.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/ResolutionTask.java @@ -58,10 +58,11 @@ public class ResolutionTask extends C @NotNull StatementFilter statementFilter, @NotNull Collection> resolvedCalls, boolean isAnnotationContext, - boolean collectAllCandidates + boolean collectAllCandidates, + @NotNull CallPosition callPosition ) { super(trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, - dataFlowInfoForArguments, callChecker, statementFilter, isAnnotationContext, collectAllCandidates); + dataFlowInfoForArguments, callChecker, statementFilter, isAnnotationContext, collectAllCandidates, callPosition); this.lazyCandidates = lazyCandidates; this.resolvedCalls = resolvedCalls; this.tracing = tracing; @@ -78,7 +79,7 @@ public class ResolutionTask extends C context.resolutionResultsCache, context.dataFlowInfoForArguments, context.callChecker, context.statementFilter, new SmartList>(), - context.isAnnotationContext, context.collectAllCandidates); + context.isAnnotationContext, context.collectAllCandidates, context.callPosition); } @NotNull @@ -104,14 +105,15 @@ public class ResolutionTask extends C @NotNull ContextDependency contextDependency, @NotNull ResolutionResultsCache resolutionResultsCache, @NotNull StatementFilter statementFilter, - boolean collectAllCandidates + boolean collectAllCandidates, + @NotNull CallPosition callPosition ) { return new ResolutionTask( lazyCandidates, tracing, trace, scope, call, expectedType, dataFlowInfo, contextDependency, checkArguments, resolutionResultsCache, dataFlowInfoForArguments, callChecker, statementFilter, resolvedCalls, - isAnnotationContext, collectAllCandidates); + isAnnotationContext, collectAllCandidates, callPosition); } @Override diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategy.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategy.java index f06870c1362..4056edc3183 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategy.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.Call; import org.jetbrains.kotlin.resolve.BindingTrace; +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; @@ -52,7 +53,12 @@ public interface TracingStrategy { public void missingReceiver(@NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor expectedReceiver) {} @Override - public void wrongReceiverType(@NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor receiverParameter, @NotNull ReceiverValue receiverArgument) {} + public void wrongReceiverType( + @NotNull BindingTrace trace, + @NotNull ReceiverParameterDescriptor receiverParameter, + @NotNull ReceiverValue receiverArgument, + @NotNull ResolutionContext c + ) {} @Override public void noReceiverAllowed(@NotNull BindingTrace trace) {} @@ -95,7 +101,7 @@ public interface TracingStrategy { public void invisibleMember(@NotNull BindingTrace trace, @NotNull DeclarationDescriptorWithVisibility descriptor) {} @Override - public void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData) {} + public void typeInferenceFailed(@NotNull ResolutionContext context, @NotNull InferenceErrorData inferenceErrorData) {} @Override public void nonExtensionFunctionCalledAsExtension(@NotNull BindingTrace trace) { } @@ -115,7 +121,12 @@ public interface TracingStrategy { void missingReceiver(@NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor expectedReceiver); - void wrongReceiverType(@NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor receiverParameter, @NotNull ReceiverValue receiverArgument); + void wrongReceiverType( + @NotNull BindingTrace trace, + @NotNull ReceiverParameterDescriptor receiverParameter, + @NotNull ReceiverValue receiverArgument, + @NotNull ResolutionContext c + ); void noReceiverAllowed(@NotNull BindingTrace trace); @@ -146,7 +157,7 @@ public interface TracingStrategy { void invisibleMember(@NotNull BindingTrace trace, @NotNull DeclarationDescriptorWithVisibility descriptor); - void typeInferenceFailed(@NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData); + void typeInferenceFailed(@NotNull ResolutionContext context, @NotNull InferenceErrorData inferenceErrorData); void nonExtensionFunctionCalledAsExtension(@NotNull BindingTrace trace); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForImplicitConstructorDelegationCall.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForImplicitConstructorDelegationCall.kt index f485c3730bb..30da5b14ffb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForImplicitConstructorDelegationCall.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForImplicitConstructorDelegationCall.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext.CALL import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET import org.jetbrains.kotlin.resolve.BindingContext.RESOLVED_CALL import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue @@ -117,7 +118,7 @@ class TracingStrategyForImplicitConstructorDelegationCall( unexpectedError("missingReceiver") } - override fun wrongReceiverType(trace: BindingTrace, receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue) { + override fun wrongReceiverType(trace: BindingTrace, receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue, c: ResolutionContext<*>) { unexpectedError("wrongReceiverType") } @@ -129,7 +130,7 @@ class TracingStrategyForImplicitConstructorDelegationCall( unexpectedError("wrongNumberOfTypeArguments") } - override fun typeInferenceFailed(trace: BindingTrace, data: InferenceErrorData) { + override fun typeInferenceFailed(context: ResolutionContext<*>, data: InferenceErrorData) { unexpectedError("typeInferenceFailed") } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstantChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstantChecker.java index d242d3a30b0..0cdfa7574b7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstantChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/CompileTimeConstantChecker.java @@ -24,12 +24,15 @@ import org.jetbrains.kotlin.KtNodeTypes; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory; +import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt; import org.jetbrains.kotlin.psi.KtConstantExpression; import org.jetbrains.kotlin.psi.KtElement; import org.jetbrains.kotlin.resolve.BindingTrace; +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; +import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; import java.util.Set; @@ -42,15 +45,17 @@ public class CompileTimeConstantChecker { private final KotlinBuiltIns builtIns; private final BindingTrace trace; private final boolean checkOnlyErrorsThatDependOnExpectedType; + private final ResolutionContext context; public CompileTimeConstantChecker( - @NotNull BindingTrace trace, + @NotNull ResolutionContext context, @NotNull KotlinBuiltIns builtIns, boolean checkOnlyErrorsThatDependOnExpectedType ) { this.checkOnlyErrorsThatDependOnExpectedType = checkOnlyErrorsThatDependOnExpectedType; this.builtIns = builtIns; - this.trace = trace; + this.trace = context.trace; + this.context = context; } // return true if there is an error @@ -95,7 +100,7 @@ public class CompileTimeConstantChecker { if (!noExpectedTypeOrError(expectedType)) { KotlinType valueType = value.getType(); if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { - return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "integer", expectedType)); + return reportConstantExpectedTypeMismatch(expression, "integer", expectedType, null); } } return false; @@ -112,7 +117,7 @@ public class CompileTimeConstantChecker { if (!noExpectedTypeOrError(expectedType)) { KotlinType valueType = value.getType(); if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { - return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "floating-point", expectedType)); + return reportConstantExpectedTypeMismatch(expression, "floating-point", expectedType, null); } } return false; @@ -124,7 +129,7 @@ public class CompileTimeConstantChecker { ) { if (!noExpectedTypeOrError(expectedType) && !KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.getBooleanType(), expectedType)) { - return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "boolean", expectedType)); + return reportConstantExpectedTypeMismatch(expression, "boolean", expectedType, builtIns.getBooleanType()); } return false; } @@ -132,7 +137,7 @@ public class CompileTimeConstantChecker { private boolean checkCharValue(ConstantValue constant, KotlinType expectedType, KtConstantExpression expression) { if (!noExpectedTypeOrError(expectedType) && !KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.getCharType(), expectedType)) { - return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "character", expectedType)); + return reportConstantExpectedTypeMismatch(expression, "character", expectedType, builtIns.getCharType()); } if (constant != null) { @@ -148,6 +153,9 @@ public class CompileTimeConstantChecker { private boolean checkNullValue(@NotNull KotlinType expectedType, @NotNull KtConstantExpression expression) { if (!noExpectedTypeOrError(expectedType) && !TypeUtils.acceptsNullable(expectedType)) { + if (DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection(context, expression, expectedType, builtIns.getNullableNothingType())) { + return true; + } return reportError(NULL_FOR_NONNULL_TYPE.on(expression, expectedType)); } return false; @@ -269,10 +277,22 @@ public class CompileTimeConstantChecker { return null; } - public static boolean noExpectedTypeOrError(KotlinType expectedType) { + private static boolean noExpectedTypeOrError(KotlinType expectedType) { return TypeUtils.noExpectedType(expectedType) || expectedType.isError(); } + private boolean reportConstantExpectedTypeMismatch( + @NotNull KtConstantExpression expression, + @NotNull String typeName, + @NotNull KotlinType expectedType, + @Nullable KotlinType expressionType + ) { + if (DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection(context, expression, expectedType, expressionType)) return true; + + trace.report(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, typeName, expectedType)); + return true; + } + private boolean reportError(@NotNull Diagnostic diagnostic) { if (!checkOnlyErrorsThatDependOnExpectedType || errorsThatDependOnExpectedType.contains(diagnostic.getFactory())) { trace.report(diagnostic); 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 1d1159925f5..ad5a4d5e067 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.types.expressions; import com.google.common.collect.Lists; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; @@ -31,6 +32,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.diagnostics.Diagnostic; +import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; @@ -196,7 +198,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { ); if (!(compileTimeConstant instanceof IntegerValueTypeConstant)) { - CompileTimeConstantChecker constantChecker = new CompileTimeConstantChecker(context.trace, components.builtIns, false); + CompileTimeConstantChecker constantChecker = new CompileTimeConstantChecker(context, components.builtIns, false); ConstantValue constantValue = compileTimeConstant != null ? ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue() : null; boolean hasError = constantChecker.checkConstantExpressionType(constantValue, expression, context.expectedType); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java index af10249a880..e772cf90599 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.calls.CallResolver; +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemStatus; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintsUtil; @@ -386,7 +387,7 @@ public class ControlStructureTypingUtils { @Override public void typeInferenceFailed( - @NotNull BindingTrace trace, @NotNull InferenceErrorData data + @NotNull ResolutionContext context, @NotNull InferenceErrorData data ) { ConstraintSystem constraintSystem = data.constraintSystem; ConstraintSystemStatus status = constraintSystem.getStatus(); @@ -398,7 +399,7 @@ public class ControlStructureTypingUtils { KtExpression expression = (KtExpression) call.getCallElement(); if (status.hasOnlyErrorsDerivedFrom(EXPECTED_TYPE_POSITION) || status.hasConflictingConstraints() || status.hasTypeInferenceIncorporationError()) { // todo after KT-... remove this line - expression.accept(checkTypeVisitor, new CheckTypeContext(trace, data.expectedType)); + expression.accept(checkTypeVisitor, new CheckTypeContext(context.trace, data.expectedType)); return; } KtDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(expression, KtNamedDeclaration.class); @@ -455,7 +456,10 @@ public class ControlStructureTypingUtils { @Override public void wrongReceiverType( - @NotNull BindingTrace trace, @NotNull ReceiverParameterDescriptor receiverParameter, @NotNull ReceiverValue receiverArgument + @NotNull BindingTrace trace, + @NotNull ReceiverParameterDescriptor receiverParameter, + @NotNull ReceiverValue receiverArgument, + @NotNull ResolutionContext c ) { logError(); } @@ -532,7 +536,7 @@ public class ControlStructureTypingUtils { @Override public void typeInferenceFailed( - @NotNull BindingTrace trace, @NotNull InferenceErrorData inferenceErrorData + @NotNull ResolutionContext context, @NotNull InferenceErrorData inferenceErrorData ) { logError(); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index 5ebeb7c3ef8..f96ad4f5ea9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -22,6 +22,7 @@ import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; @@ -196,7 +197,7 @@ public class DataFlowAnalyzer { if (expression instanceof KtConstantExpression) { ConstantValue constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, c.trace, c.expectedType); - boolean error = new CompileTimeConstantChecker(c.trace, builtIns, true) + boolean error = new CompileTimeConstantChecker(c, builtIns, true) .checkConstantExpressionType(constantValue, (KtConstantExpression) expression, c.expectedType); hasError.set(error); return expressionType; @@ -210,7 +211,9 @@ public class DataFlowAnalyzer { SmartCastResult castResult = checkPossibleCast(expressionType, expression, c); if (castResult != null) return castResult.getResultType(); - c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType)); + if (!DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection(c, expression, c.expectedType, expressionType)) { + c.trace.report(TYPE_MISMATCH.on(expression, c.expectedType, expressionType)); + } hasError.set(true); return expressionType; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java index dadb1e6329c..d97c6e949a3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingContext.java @@ -20,10 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.StatementFilter; import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker; -import org.jetbrains.kotlin.resolve.calls.context.ContextDependency; -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; -import org.jetbrains.kotlin.resolve.calls.context.ResolutionResultsCache; -import org.jetbrains.kotlin.resolve.calls.context.ResolutionResultsCacheImpl; +import org.jetbrains.kotlin.resolve.calls.context.*; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.scopes.LexicalScope; import org.jetbrains.kotlin.types.KotlinType; @@ -61,8 +58,8 @@ public class ExpressionTypingContext extends ResolutionContext factory = diagnostic.getFactory(); - if ((factory == TYPE_MISMATCH || factory == CONSTANT_EXPECTED_TYPE_MISMATCH || factory == NULL_FOR_NONNULL_TYPE) - && diagnostic.getPsiElement() == expressionToWatch) { + if (Errors.TYPE_MISMATCH_ERRORS.contains(factory) && diagnostic.getPsiElement() == expressionToWatch) { mismatchFound[0] = true; } if (TYPE_INFERENCE_ERRORS.contains(factory) && diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java index e382d37e015..7e07c069bbb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; +import org.jetbrains.kotlin.resolve.calls.context.CallPosition; import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind; import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope; import org.jetbrains.kotlin.types.DeferredType; @@ -230,7 +231,8 @@ public abstract class ExpressionTypingVisitorDispatcher extends KtVisitor = { x -> x.toString() } - f(1) + f(1) } diff --git a/compiler/testData/diagnostics/tests/generics/Projections.kt b/compiler/testData/diagnostics/tests/generics/Projections.kt index 9dfc8a80d30..0dfd5a8fd99 100644 --- a/compiler/testData/diagnostics/tests/generics/Projections.kt +++ b/compiler/testData/diagnostics/tests/generics/Projections.kt @@ -36,13 +36,13 @@ fun testInOut() { Inv().f(1) (null as Inv).f(1) - (null as Inv).f(1) // !! - (null as Inv<*>).f(1) // !! + (null as Inv).f(1) // !! + (null as Inv<*>).f(1) // !! Inv().inf(1) (null as Inv).inf(1) - (null as Inv).inf(1) // !! - (null as Inv<*>).inf(1) // !! + (null as Inv).inf(1) // !! + (null as Inv<*>).inf(1) // !! Inv().outf() checkSubtype((null as Inv).outf()) // Type mismatch diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/simpleOutUseSite.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/simpleOutUseSite.kt index 76e834d15c0..eb6bf8de9f7 100644 --- a/compiler/testData/diagnostics/tests/generics/innerClasses/simpleOutUseSite.kt +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/simpleOutUseSite.kt @@ -28,9 +28,9 @@ fun main() { checkSubtype.Inner>(outer.bar()) checkSubtype.Inner>(outer.Inner()) - outer.set(.Inner)!>outer.bar()) - outer.set(.Inner)!>outer.Inner()) + outer.set(outer.bar()) + outer.set(outer.Inner()) val x: Outer.Inner = factoryString() - outer.set(.Inner)!>x) + outer.set(x) } diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.kt index e6e4396601b..484074fe096 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.kt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.kt @@ -4,6 +4,7 @@ interface C interface MC : C { fun addAll(x: C): Boolean fun addAllMC(x: MC): Boolean + fun addAllInv(x: MC): Boolean } interface Open @@ -13,15 +14,18 @@ fun mc(): MC = null!! fun c(): C = null!! fun foo(x: MC) { - x.addAll(; MC)!>x) - x.addAllMC(; MC)!>x) + x.addAll(; MC; MC; public abstract fun addAll\(x: C\): kotlin.Boolean defined in MC)!>x) + x.addAllMC(; MC; MC; public abstract fun addAllMC\(x: MC\): kotlin.Boolean defined in MC)!>x) - x.addAll(; MC)!>mc()) - x.addAllMC(; MC)!>mc()) + x.addAll(; MC; MC; public abstract fun addAll\(x: C\): kotlin.Boolean defined in MC)!>mc()) + x.addAllMC(; MC; MC; public abstract fun addAllMC\(x: MC\): kotlin.Boolean defined in MC)!>mc()) - x.addAll(; MC)!>mc()) - x.addAllMC(; MC)!>mc()) + x.addAll(; MC; MC; public abstract fun addAll\(x: C\): kotlin.Boolean defined in MC)!>mc()) + x.addAllMC(; MC; MC; public abstract fun addAllMC\(x: MC\): kotlin.Boolean defined in MC)!>mc()) x.addAll(c()) x.addAll(c()) + + x.addAllInv(mc()) + x.addAll(1) } diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.txt index ae151570fd4..44921e1a4a2 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.txt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/addAll.txt @@ -19,6 +19,7 @@ public final class Derived : Open { public interface MC : C { public abstract fun addAll(/*0*/ x: C): kotlin.Boolean + public abstract fun addAllInv(/*0*/ x: MC): kotlin.Boolean public abstract fun addAllMC(/*0*/ x: MC): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.kt new file mode 100644 index 00000000000..8f9c3ffb23d --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.kt @@ -0,0 +1,19 @@ +class A { + fun T.foo() {} + fun Out.bar() {} +} +class Out + +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +fun test(x: A, y: Out) { + with(x) { + // TODO: this diagnostic could be replaced with TYPE_MISMATCH_DUE_TO_TYPE_PROJECTION + "".foo() + y.bar() + + with(y) { + bar() + } + } +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.txt new file mode 100644 index 00000000000..a3a2d73f56c --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.txt @@ -0,0 +1,20 @@ +package + +public fun test(/*0*/ x: A, /*1*/ y: Out): kotlin.Unit +public inline fun with(/*0*/ receiver: T, /*1*/ f: T.() -> R): R + +public final class A { + public constructor A() + 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 fun Out.bar(): kotlin.Unit + public final fun T.foo(): kotlin.Unit +} + +public final class Out { + public constructor Out() + 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/generics/projectionsScope/inValueParameter.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.kt new file mode 100644 index 00000000000..de5ad6f737b --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.kt @@ -0,0 +1,8 @@ +interface A +interface B { + fun foo(x: A) +} + +fun foo(x: B, y: A) { + x.foo(y) +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.txt new file mode 100644 index 00000000000..a94f04f1489 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.txt @@ -0,0 +1,16 @@ +package + +public fun foo(/*0*/ x: B, /*1*/ y: A): kotlin.Unit + +public interface A { + 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 interface B { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(/*0*/ x: A): kotlin.Unit + 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/generics/projectionsScope/leakedApproximatedType.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.kt new file mode 100644 index 00000000000..06e8aaba45e --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.kt @@ -0,0 +1,19 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Out +class In { + fun invoke1(x: T) {} + fun invoke2(x: Out) {} +} + +interface A { + fun foo(): In +} + +fun test(a: A, y: Out) { + val i = a.foo() + // TODO: These diagnostic are wrong, type of 'i' --- 'In' is not projected itself, + // but it's approximation result caused by 'a' projection + i.invoke1("") + i.invoke2(y) +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.txt new file mode 100644 index 00000000000..108765adb78 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.txt @@ -0,0 +1,26 @@ +package + +public fun test(/*0*/ a: A, /*1*/ y: Out): kotlin.Unit + +public interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): In + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class In { + public constructor In() + 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 final fun invoke1(/*0*/ x: T): kotlin.Unit + public final fun invoke2(/*0*/ x: Out): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Out { + public constructor Out() + 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/generics/projectionsScope/multipleArgumentProjectedOut.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.kt new file mode 100644 index 00000000000..08cb1fceb7d --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.kt @@ -0,0 +1,9 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class A { + fun foo(x: T, y: T) {} +} + +fun test(a: A) { + a.foo("", "") +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.txt new file mode 100644 index 00000000000..fbe42f65314 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.txt @@ -0,0 +1,11 @@ +package + +public fun test(/*0*/ a: A): kotlin.Unit + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ x: T, /*1*/ y: T): kotlin.Unit + 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/generics/projectionsScope/projectedOutConventions.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.kt new file mode 100644 index 00000000000..21fd8ad5a84 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.kt @@ -0,0 +1,13 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class A { + operator fun plus(x: T): A = this + operator fun set(x: Int, y: T) {} + operator fun get(x: T) = 1 +} + +fun test(a: A) { + a defined in A; A)!>+ "" + )!>a[1] = "" + )!>a[""] +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.txt new file mode 100644 index 00000000000..0172cacb9dd --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.txt @@ -0,0 +1,13 @@ +package + +public fun test(/*0*/ a: A): kotlin.Unit + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun get(/*0*/ x: T): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun plus(/*0*/ x: T): A + public final operator fun set(/*0*/ x: kotlin.Int, /*1*/ y: T): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.kt new file mode 100644 index 00000000000..9dde6a18ee4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.kt @@ -0,0 +1,19 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class A { + operator fun plus(x: Out): A = this + operator fun set(x: Int, y: Out) {} + operator fun get(x: Out) = 1 +} + +class Out + +fun test(a: A, y: Out) { + a + y + a[1] = y + a[y] + + a + Out() + a[1] = Out() + a[Out()] +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.txt new file mode 100644 index 00000000000..fc8efb9f880 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.txt @@ -0,0 +1,20 @@ +package + +public fun test(/*0*/ a: A, /*1*/ y: Out): kotlin.Unit + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun get(/*0*/ x: Out): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun plus(/*0*/ x: Out): A + public final operator fun set(/*0*/ x: kotlin.Int, /*1*/ y: Out): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Out { + public constructor Out() + 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/generics/projectionsScope/typeMismatchInLambda.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.kt new file mode 100644 index 00000000000..b56674cf621 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.kt @@ -0,0 +1,26 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + +class Out { + fun id() = this + fun foobar(x: Any) {} +} + +class A { + inline fun foo(block: () -> E) {} + inline fun bar(block: () -> Out) {} +} + +fun test(a: A, z: Out) { + a.foo { + val x: String = 1 // Should be no TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS + "" + } + a.bar { Out() } + a.bar { Out() } + a.bar { z.id() } + + a.foo { + z.foobar(if (1 > 2) return@foo "" else "") + "" + } +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.txt new file mode 100644 index 00000000000..0446ad7fb14 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.txt @@ -0,0 +1,21 @@ +package + +public fun test(/*0*/ a: A, /*1*/ z: Out): kotlin.Unit + +public final class A { + public constructor A() + public final inline fun bar(/*0*/ block: () -> Out): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final inline fun foo(/*0*/ block: () -> E): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Out { + public constructor Out() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foobar(/*0*/ x: kotlin.Any): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun id(): Out + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.kt new file mode 100644 index 00000000000..cc4f65e127e --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class A { + fun foo(vararg x: T) {} +} + +fun test(a: A, y: Array) { + a.foo("", "", "") + a.foo(*y) + // TODO: TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS probably redundant + a.foo(*y, "") +} diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.txt b/compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.txt new file mode 100644 index 00000000000..863d6061de0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.txt @@ -0,0 +1,11 @@ +package + +public fun test(/*0*/ a: A, /*1*/ y: kotlin.Array): kotlin.Unit + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ vararg x: T /*kotlin.Array*/): kotlin.Unit + 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/generics/varProjection/setterNotProjectedOutAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt index 77cc96edec7..4013acd1a59 100644 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterNotProjectedOutAssign.kt @@ -4,6 +4,6 @@ interface Tr { } fun test(t: Tr<*>) { - t.v = t + t.v = t t.v checkType { _>() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt index 39375e09230..be7dcf38e55 100644 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutAssign.kt @@ -7,7 +7,7 @@ interface Tr { fun test(t: Tr<*>) { t.v = null!! - t.v = "" - t.v = null + t.v = "" + t.v = null t.v checkType { _() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt index f6a120a79cd..f1169423b91 100644 --- a/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt +++ b/compiler/testData/diagnostics/tests/generics/varProjection/setterProjectedOutNoPlusAssign.kt @@ -6,5 +6,5 @@ interface Tr { fun test(t: Tr) { // resolved as t.v = t.v + null!!, where type of right operand is String, // so TYPE_MISMATCH: String is not <: of Captured(out String) - t.v += null!! + t.v += null!! } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/addAllProjection.kt b/compiler/testData/diagnostics/testsWithStdLib/addAllProjection.kt index f9445104988..f3824331f50 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/addAllProjection.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/addAllProjection.kt @@ -1,12 +1,12 @@ fun test(mc: MutableCollection) { - mc.addAll(mc) + mc.addAll(mc) - mc.addAll(arrayListOf()) + mc.addAll(arrayListOf()) mc.addAll(arrayListOf()) - mc.addAll(listOf("")) - mc.addAll(listOf("")) - mc.addAll(listOf("")) + mc.addAll(listOf("")) + mc.addAll(listOf("")) + mc.addAll(listOf("")) mc.addAll(emptyList()) mc.addAll(emptyList()) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 1722725a684..5b1b658b7ee 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -7190,6 +7190,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("extensionReceiverTypeMismatch.kt") + public void testExtensionReceiverTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/extensionReceiverTypeMismatch.kt"); + doTest(fileName); + } + @TestMetadata("extensionResultSubstitution.kt") public void testExtensionResultSubstitution() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/extensionResultSubstitution.kt"); @@ -7202,6 +7208,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("inValueParameter.kt") + public void testInValueParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/inValueParameter.kt"); + doTest(fileName); + } + @TestMetadata("kt7296.kt") public void testKt7296() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/kt7296.kt"); @@ -7220,18 +7232,36 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("leakedApproximatedType.kt") + public void testLeakedApproximatedType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/leakedApproximatedType.kt"); + doTest(fileName); + } + @TestMetadata("MLOut.kt") public void testMLOut() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/MLOut.kt"); doTest(fileName); } + @TestMetadata("multipleArgumentProjectedOut.kt") + public void testMultipleArgumentProjectedOut() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/multipleArgumentProjectedOut.kt"); + doTest(fileName); + } + @TestMetadata("platformSuperClass.kt") public void testPlatformSuperClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.kt"); doTest(fileName); } + @TestMetadata("projectedOutConventions.kt") + public void testProjectedOutConventions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/projectedOutConventions.kt"); + doTest(fileName); + } + @TestMetadata("recursiveUpperBoundStar.kt") public void testRecursiveUpperBoundStar() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt"); @@ -7262,6 +7292,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typeMismatchConventions.kt") + public void testTypeMismatchConventions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchConventions.kt"); + doTest(fileName); + } + + @TestMetadata("typeMismatchInLambda.kt") + public void testTypeMismatchInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/typeMismatchInLambda.kt"); + doTest(fileName); + } + @TestMetadata("typeParameterBounds.kt") public void testTypeParameterBounds() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/typeParameterBounds.kt"); @@ -7273,6 +7315,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceStar.kt"); doTest(fileName); } + + @TestMetadata("varargs.kt") + public void testVarargs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/varargs.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/generics/starProjections") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/CapturedTypeConstructor.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/CapturedTypeConstructor.kt index cdc6da5bd6e..c4f88d2ae9f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/CapturedTypeConstructor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/CapturedTypeConstructor.kt @@ -98,17 +98,17 @@ fun createCapturedType(typeProjection: TypeProjection): KotlinType fun KotlinType.isCaptured(): Boolean = constructor is CapturedTypeConstructor -fun TypeSubstitution.wrapWithCapturingSubstitution(): TypeSubstitution = +fun TypeSubstitution.wrapWithCapturingSubstitution(needApproximation: Boolean = true): TypeSubstitution = if (this is IndexedParametersSubstitution) IndexedParametersSubstitution( this.parameters, this.arguments.zip(this.parameters).map { it.first.createCapturedIfNeeded(it.second) }.toTypedArray(), - approximateCapturedTypes = true) + approximateCapturedTypes = needApproximation) else object : DelegatedTypeSubstitution(this@wrapWithCapturingSubstitution) { - override fun approximateContravariantCapturedTypes() = true + override fun approximateContravariantCapturedTypes() = needApproximation override fun get(key: KotlinType) = super.get(key)?.createCapturedIfNeeded(key.constructor.declarationDescriptor as? TypeParameterDescriptor) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt b/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt index 296127df9c9..2c9cda2aac8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/CapturedTypeApproximation.kt @@ -23,7 +23,8 @@ import org.jetbrains.kotlin.resolve.calls.inference.isCaptured import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.* +import org.jetbrains.kotlin.utils.addToStdlib.check import java.util.* data class ApproximationBounds( diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java index 6bc96579aab..f54515c2335 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java @@ -20,12 +20,14 @@ import com.intellij.icons.AllIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; import org.jetbrains.kotlin.diagnostics.Diagnostic; +import org.jetbrains.kotlin.diagnostics.TypeMismatchDueToTypeProjectionsData; import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages; import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap; import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer; import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs; import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallDataHtmlRenderer; import org.jetbrains.kotlin.renderer.DescriptorRenderer; +import org.jetbrains.kotlin.renderer.MultiRenderer; import java.net.URL; @@ -66,6 +68,22 @@ public class IdeErrorMessages { MAP.put(TYPE_MISMATCH, "Type mismatch.
Required:{0}
Found:{1}
", HTML_RENDER_TYPE, HTML_RENDER_TYPE); + MAP.put(TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS, + "Type mismatch.
Required:{0}
Found:{1}

\n" + + "Projected type {2} restricts use of
\n{3}\n", + new MultiRenderer() { + @NotNull + @Override + public String[] render(@NotNull TypeMismatchDueToTypeProjectionsData object) { + return new String[] { + HTML_RENDER_TYPE.render(object.getExpectedType()), + HTML_RENDER_TYPE.render(object.getExpressionType()), + HTML_RENDER_TYPE.render(object.getReceiverType()), + DescriptorRenderer.HTML.render(object.getCallableDescriptor()) + }; + } + }); + MAP.put(ASSIGN_OPERATOR_AMBIGUITY, "Assignment operators ambiguity. All these functions match.
    {0}
", HTML_AMBIGUOUS_CALLS); diff --git a/idea/testData/diagnosticMessage/expectedNothingDueToProjections.kt b/idea/testData/diagnosticMessage/expectedNothingDueToProjections.kt new file mode 100644 index 00000000000..e62270da9d3 --- /dev/null +++ b/idea/testData/diagnosticMessage/expectedNothingDueToProjections.kt @@ -0,0 +1,6 @@ +// !DIAGNOSTICS_NUMBER: 1 +// !DIAGNOSTICS: MEMBER_PROJECTED_OUT + +fun foo(x: MutableCollection) { + x.add("") +} diff --git a/idea/testData/diagnosticMessage/expectedNothingDueToProjections1.txt b/idea/testData/diagnosticMessage/expectedNothingDueToProjections1.txt new file mode 100644 index 00000000000..01e159a607f --- /dev/null +++ b/idea/testData/diagnosticMessage/expectedNothingDueToProjections1.txt @@ -0,0 +1,2 @@ + +Out-projected type 'kotlin.MutableCollection' prohibits the use of 'public abstract fun add(element: E): kotlin.Boolean defined in kotlin.MutableCollection' diff --git a/idea/testData/diagnosticMessage/typeMismatchDueToProjections.kt b/idea/testData/diagnosticMessage/typeMismatchDueToProjections.kt new file mode 100644 index 00000000000..ac21e954fa2 --- /dev/null +++ b/idea/testData/diagnosticMessage/typeMismatchDueToProjections.kt @@ -0,0 +1,6 @@ +// !DIAGNOSTICS_NUMBER: 1 +// !DIAGNOSTICS: TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS + +fun foo(x: MutableCollection, y: MutableCollection) { + x.addAll(y) +} diff --git a/idea/testData/diagnosticMessage/typeMismatchDueToProjections1.html b/idea/testData/diagnosticMessage/typeMismatchDueToProjections1.html new file mode 100644 index 00000000000..e4e75d9cb32 --- /dev/null +++ b/idea/testData/diagnosticMessage/typeMismatchDueToProjections1.html @@ -0,0 +1,17 @@ + + +Type mismatch. + + + + + + + + + +
Required:kotlin.Collection<`kotlin.Nothing>
Found:kotlin.MutableCollection<`kotlin.CharSequence>
+
+Projected type kotlin.MutableCollection<`out kotlin.CharSequence> restricts use of
+public abstract fun addAll(elements: kotlin.Collection<`E>): kotlin.Boolean defined in kotlin.MutableCollection + \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn.kt b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn.kt new file mode 100644 index 00000000000..05a49515eb6 --- /dev/null +++ b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn.kt @@ -0,0 +1,11 @@ +// !DIAGNOSTICS_NUMBER: 1 +// !DIAGNOSTICS: TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS + +interface A +interface B { + fun foo(x: A) +} + +fun foo(x: B, y: A) { + x.foo(y) +} diff --git a/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn1.html b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn1.html new file mode 100644 index 00000000000..7484ec1016a --- /dev/null +++ b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn1.html @@ -0,0 +1,17 @@ + + +Type mismatch. + + + + + + + + + +
Required:A<`kotlin.Any?>
Found:A<`kotlin.CharSequence>
+
+Projected type B<`in kotlin.CharSequence> restricts use of
+public abstract fun foo(x: A<`in E>): kotlin.Unit defined in B + \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt.kt b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt.kt new file mode 100644 index 00000000000..0a40d6fe677 --- /dev/null +++ b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt.kt @@ -0,0 +1,7 @@ +// !DIAGNOSTICS_NUMBER: 1 +// !DIAGNOSTICS: TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS +// !MESSAGE_TYPE: TEXT + +fun foo(x: MutableCollection, y: MutableCollection) { + x.addAll(y) +} diff --git a/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt1.txt b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt1.txt new file mode 100644 index 00000000000..2c686e98185 --- /dev/null +++ b/idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt1.txt @@ -0,0 +1,2 @@ + +Type mismatch: inferred type is kotlin.MutableCollection but kotlin.Collection was expected. Projected type kotlin.MutableCollection restricts use of public abstract fun addAll(elements: kotlin.Collection): kotlin.Boolean defined in kotlin.MutableCollection \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/highlighter/DiagnosticMessageTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/highlighter/DiagnosticMessageTestGenerated.java index 26aec84d2bb..5cb1447ba2e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/highlighter/DiagnosticMessageTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/highlighter/DiagnosticMessageTestGenerated.java @@ -101,6 +101,12 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes doTest(fileName); } + @TestMetadata("expectedNothingDueToProjections.kt") + public void testExpectedNothingDueToProjections() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/expectedNothingDueToProjections.kt"); + doTest(fileName); + } + @TestMetadata("extensionInClassReference.kt") public void testExtensionInClassReference() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/extensionInClassReference.kt"); @@ -179,6 +185,24 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes doTest(fileName); } + @TestMetadata("typeMismatchDueToProjections.kt") + public void testTypeMismatchDueToProjections() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeMismatchDueToProjections.kt"); + doTest(fileName); + } + + @TestMetadata("typeMismatchDueToProjectionsIn.kt") + public void testTypeMismatchDueToProjectionsIn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeMismatchDueToProjectionsIn.kt"); + doTest(fileName); + } + + @TestMetadata("typeMismatchDueToProjectionsTxt.kt") + public void testTypeMismatchDueToProjectionsTxt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeMismatchDueToProjectionsTxt.kt"); + doTest(fileName); + } + @TestMetadata("typeMismatchWithNothing.kt") public void testTypeMismatchWithNothing() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeMismatchWithNothing.kt");