From 7aa2642a2bdfb154093312308eeb66c2ec9a9cfd Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 23 Mar 2015 10:56:23 +0300 Subject: [PATCH] Unify reporting resolution errors for empty delegation calls - Always report "There is no applicable constructor for call without arguments in superclass" under `constructor` keyword by replacing default TracingStrategy - Remove positioning strategy used for reporting non-applicable for empty delegation calls #KT-6971 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 4 +- .../diagnostics/PositioningStrategies.kt | 16 +- .../rendering/DefaultErrorMessages.java | 2 + .../kotlin/resolve/calls/CallResolver.java | 20 ++- ...rategyForEmptyConstructorDelegationCall.kt | 146 ++++++++++++++++++ .../errorsOnEmptyDelegationCall.kt | 42 +++++ .../errorsOnEmptyDelegationCall.txt | 63 ++++++++ .../nestedExtendsInner.kt | 7 + .../nestedExtendsInner.txt | 22 +++ .../superSecondaryNonExisting.kt | 2 +- .../checkers/JetDiagnosticsTestGenerated.java | 12 ++ 11 files changed, 318 insertions(+), 18 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForEmptyConstructorDelegationCall.kt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.kt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.txt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.kt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index ea6aa27bf86..78b9fc0d3fa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -169,6 +169,7 @@ public interface Errors { DiagnosticFactory0.create(ERROR, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL); DiagnosticFactory0 PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 EXPLICIT_DELEGATION_CALL_REQUIRED = DiagnosticFactory0.create(ERROR); // Trait-specific @@ -411,8 +412,7 @@ public interface Errors { DiagnosticFactory0 NOT_A_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory1>> OVERLOAD_RESOLUTION_AMBIGUITY = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1>> NONE_APPLICABLE = - DiagnosticFactory1.create(ERROR, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL_OR_DEFAULT); + DiagnosticFactory1>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1>> UNRESOLVED_REFERENCE_WRONG_RECEIVER = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 7d659246c63..52038e96d3d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -62,6 +62,9 @@ public object PositioningStrategies { element.getNameIdentifier() ?: element.getObjectKeyword() ) } + is JetConstructorDelegationCall -> { + return SECONDARY_CONSTRUCTOR_DELEGATION_CALL.mark(element) + } else -> { return super.mark(element) } @@ -422,17 +425,4 @@ public object PositioningStrategies { return markElement(element.getCalleeExpression() ?: element) } } - - public val SECONDARY_CONSTRUCTOR_DELEGATION_CALL_OR_DEFAULT: PositioningStrategy = - object : PositioningStrategy() { - override fun mark(element: PsiElement): List { - val parent = element.getParent() - if (parent is JetConstructorDelegationCall) { - return SECONDARY_CONSTRUCTOR_DELEGATION_CALL.mark(parent) - } - else { - return DEFAULT.mark(element) - } - } - } } 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 7a05ab5ea94..34548a6c7b8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -397,6 +397,8 @@ public class DefaultErrorMessages { MAP.put(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED, "Primary constructor call expected"); MAP.put(DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR, "Call to super is not allowed in enum constructor"); MAP.put(PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS, "Primary constructor required for data class"); + MAP.put(EXPLICIT_DELEGATION_CALL_REQUIRED, + "Explicit 'this' or 'super' call is required. There is no constructor in superclass that can be called without arguments"); MAP.put(INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED, "Expecting 'init' keyword before class initializer"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index 9b6fb2758ff..abfa79607c6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -188,7 +188,17 @@ public class CallResolver { @NotNull Collection> candidates, @NotNull CallTransformer callTransformer ) { - TracingStrategy tracing = TracingStrategyImpl.create(referenceExpression, context.call); + return computeTasksFromCandidatesAndResolvedCall(context, candidates, callTransformer, + TracingStrategyImpl.create(referenceExpression, context.call)); + } + + @NotNull + private OverloadResolutionResults computeTasksFromCandidatesAndResolvedCall( + @NotNull BasicCallResolutionContext context, + @NotNull Collection> candidates, + @NotNull CallTransformer callTransformer, + @NotNull TracingStrategy tracing + ) { List> prioritizedTasks = taskPrioritizer.computePrioritizedTasksFromCandidates(context, candidates, tracing); return doResolveCallOrGetCachedResults(context, prioritizedTasks, callTransformer, tracing); @@ -313,6 +323,7 @@ public class CallResolver { return resolveConstructorDelegationCall( context, + call, call.getCalleeExpression(), constructorDescriptor ); @@ -321,6 +332,7 @@ public class CallResolver { @NotNull private OverloadResolutionResults resolveConstructorDelegationCall( @NotNull BasicCallResolutionContext context, + @NotNull JetConstructorDelegationCall call, @NotNull JetConstructorDelegationReferenceExpression calleeExpression, @NotNull ConstructorDescriptor calleeConstructor ) { @@ -364,7 +376,11 @@ public class CallResolver { knownTypeParametersSubstitutor)); } - return computeTasksFromCandidatesAndResolvedCall(context, calleeExpression, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER); + TracingStrategy tracing = calleeExpression.isEmpty() ? + new TracingStrategyForEmptyConstructorDelegationCall(call, context.call) : + TracingStrategyImpl.create(calleeExpression, context.call); + + return computeTasksFromCandidatesAndResolvedCall(context, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER, tracing); } public OverloadResolutionResults resolveCallWithKnownCandidate( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForEmptyConstructorDelegationCall.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForEmptyConstructorDelegationCall.kt new file mode 100644 index 00000000000..7fdc07acc62 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TracingStrategyForEmptyConstructorDelegationCall.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.tasks + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE +import org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER +import org.jetbrains.kotlin.psi.Call +import org.jetbrains.kotlin.psi.JetConstructorDelegationCall +import org.jetbrains.kotlin.psi.JetFunctionLiteralArgument +import org.jetbrains.kotlin.psi.JetSecondaryConstructor +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.inference.InferenceErrorData +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.JetType + + +public class TracingStrategyForEmptyConstructorDelegationCall( + val delegationCall: JetConstructorDelegationCall, call: Call +) : AbstractTracingStrategy(delegationCall.getCalleeExpression()!!, call) { + + val calleeExpression = delegationCall.getCalleeExpression() + + override fun bindCall(trace: BindingTrace, call: Call) { + trace.record(CALL, call.getCalleeExpression(), call) + } + + override fun bindReference(trace: BindingTrace, resolvedCall: ResolvedCall) { + val descriptor = resolvedCall.getCandidateDescriptor() + val storedReference = trace.get(REFERENCE_TARGET, calleeExpression) + if (storedReference == null || !ErrorUtils.isError(descriptor)) { + trace.record(REFERENCE_TARGET, calleeExpression, descriptor) + } + } + + override fun bindResolvedCall(trace: BindingTrace, resolvedCall: ResolvedCall) { + trace.record(RESOLVED_CALL, call, resolvedCall) + } + + override fun unresolvedReference(trace: BindingTrace) { + trace.report(UNRESOLVED_REFERENCE.on(calleeExpression, calleeExpression)) + } + + override fun unresolvedReferenceWrongReceiver(trace: BindingTrace, candidates: Collection>) { + trace.report(UNRESOLVED_REFERENCE_WRONG_RECEIVER.on(reference, candidates)) + } + + override fun noValueForParameter(trace: BindingTrace, valueParameter: ValueParameterDescriptor) { + reportError(trace) + } + + override fun ambiguity(trace: BindingTrace, descriptors: MutableCollection>) { + reportError(trace) + } + + override fun noneApplicable(trace: BindingTrace, descriptors: MutableCollection>) { + reportError(trace) + } + + override fun invisibleMember(trace: BindingTrace, descriptor: DeclarationDescriptorWithVisibility) { + reportError(trace) + } + + private fun reportError(trace: BindingTrace) { + val declaration = delegationCall.getParent() as JetSecondaryConstructor + trace.report(Errors.EXPLICIT_DELEGATION_CALL_REQUIRED.on(declaration.getConstructorKeyword())) + } + + // Underlying methods should not be called because such errors are impossible + // when resolving delegation call + override fun cannotCompleteResolve(trace: BindingTrace, descriptors: MutableCollection>) { + unexpectedError("cannotCompleteResolve") + } + + override fun instantiationOfAbstractClass(trace: BindingTrace) { + unexpectedError("instantiationOfAbstractClass") + } + + override fun abstractSuperCall(trace: BindingTrace) { + unexpectedError("abstractSuperCall") + } + + override fun nestedClassAccessViaInstanceReference( + trace: BindingTrace, classDescriptor: ClassDescriptor, explicitReceiverKind: ExplicitReceiverKind + ) { + unexpectedError("nestedClassAccessViaInstanceReference") + } + + override fun unsafeCall(trace: BindingTrace, type: JetType, isCallForImplicitInvoke: Boolean) { + unexpectedError("unsafeCall") + } + + override fun unnecessarySafeCall(trace: BindingTrace, type: JetType) { + unexpectedError("unnecessarySafeCall") + } + + override fun danglingFunctionLiteralArgumentSuspected( + trace: BindingTrace, functionLiteralArguments: MutableList + ) { + unexpectedError("danglingFunctionLiteralArgumentSuspected") + } + + override fun missingReceiver(trace: BindingTrace, expectedReceiver: ReceiverParameterDescriptor) { + unexpectedError("missingReceiver") + } + + override fun wrongReceiverType(trace: BindingTrace, receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue) { + unexpectedError("wrongReceiverType") + } + + override fun noReceiverAllowed(trace: BindingTrace) { + unexpectedError("noReceiverAllowed") + } + + override fun wrongNumberOfTypeArguments(trace: BindingTrace, expectedTypeArgumentCount: Int) { + unexpectedError("wrongNumberOfTypeArguments") + } + + override fun typeInferenceFailed(trace: BindingTrace, data: InferenceErrorData) { + unexpectedError("typeInferenceFailed") + } + + private fun unexpectedError(type: String) { + throw AssertionError("Unexpected error type: $type") + } +} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.kt new file mode 100644 index 00000000000..a76c5a26ed6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.kt @@ -0,0 +1,42 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +open class B0(x: Int) + +class A0 : B0 { + constructor() + constructor(x: Int) : super() +} + +// -------------------------- + +open class B1 { + constructor(x: Int = 1) + constructor() +} + +class A1 : B1 { + constructor() + constructor(x: Int) : super() +} + +// -------------------------- + +open class B2 { + constructor(x: Int) + constructor(x: String) +} + +class A2 : B2 { + constructor() + constructor(x: Int) : super() +} + +// -------------------------- + +open class B3 { + private constructor() +} + +class A3 : B3 { + constructor() + constructor(x: Int) : super() +} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.txt b/compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.txt new file mode 100644 index 00000000000..e6b1acea792 --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.txt @@ -0,0 +1,63 @@ +package + +internal final class A0 : B0 { + public constructor A0() + public constructor A0(/*0*/ x: kotlin.Int) + 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 +} + +internal final class A1 : B1 { + public constructor A1() + public constructor A1(/*0*/ x: kotlin.Int) + 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 +} + +internal final class A2 : B2 { + public constructor A2() + public constructor A2(/*0*/ x: kotlin.Int) + 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 +} + +internal final class A3 : B3 { + public constructor A3() + public constructor A3(/*0*/ x: kotlin.Int) + 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 +} + +internal open class B0 { + public constructor B0(/*0*/ x: kotlin.Int) + 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 +} + +internal open class B1 { + public constructor B1() + public constructor B1(/*0*/ x: kotlin.Int = ...) + 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 +} + +internal open class B2 { + public constructor B2(/*0*/ x: kotlin.Int) + public constructor B2(/*0*/ x: kotlin.String) + 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 +} + +internal open class B3 { + private constructor B3() + 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/secondaryConstructors/nestedExtendsInner.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.kt new file mode 100644 index 00000000000..45dfbf44d3c --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.kt @@ -0,0 +1,7 @@ +class A { + open inner class Inner + + class Nested : Inner { + constructor() + } +} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.txt b/compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.txt new file mode 100644 index 00000000000..41a8dd7fe4b --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.txt @@ -0,0 +1,22 @@ +package + +internal 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 + + internal open inner class Inner { + public constructor Inner() + 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 + } + + internal final class Nested : A.Inner { + public constructor Nested() + 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/secondaryConstructors/superSecondaryNonExisting.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/superSecondaryNonExisting.kt index fe14269ce2c..fde881c5413 100644 --- a/compiler/testData/diagnostics/tests/secondaryConstructors/superSecondaryNonExisting.kt +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/superSecondaryNonExisting.kt @@ -6,5 +6,5 @@ open class B(x: Double) { trait C class A : B, C { constructor(): super(' ') - constructor(x: Int) + constructor(x: Int) } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 0e194f976a1..ea24d646e96 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -10647,6 +10647,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("errorsOnEmptyDelegationCall.kt") + public void testErrorsOnEmptyDelegationCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.kt"); + doTest(fileName); + } + @TestMetadata("expectedInitKeywordOnInitializer.kt") public void testExpectedInitKeywordOnInitializer() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/expectedInitKeywordOnInitializer.kt"); @@ -10719,6 +10725,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("nestedExtendsInner.kt") + public void testNestedExtendsInner() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.kt"); + doTest(fileName); + } + @TestMetadata("noDefaultIfEmptySecondary.kt") public void testNoDefaultIfEmptySecondary() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/noDefaultIfEmptySecondary.kt");