Set reference target and report custom error instead unresolved reference for cases: interface, generic parameter, object + invoke, class without companion.

#KT-2787 Fixed.
This commit is contained in:
Stanislav Erokhin
2016-08-31 16:03:11 +03:00
parent eb6f444c19
commit bebc75c8ef
17 changed files with 338 additions and 12 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
import org.jetbrains.kotlin.types.KotlinType;
import java.lang.reflect.Field;
@@ -531,6 +532,9 @@ public interface Errors {
DiagnosticFactory1<PsiElement, KotlinType> REIFIED_TYPE_FORBIDDEN_SUBSTITUTION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, KotlinType> REIFIED_TYPE_UNSAFE_SUBSTITUTION = DiagnosticFactory1.create(WARNING);
DiagnosticFactory3<KtReferenceExpression, ClassifierDescriptor, WrongResolutionToClassifier, String> RESOLUTION_TO_CLASSIFIER =
DiagnosticFactory3.create(ERROR);
// Type inference
DiagnosticFactory0<KtParameter> CANNOT_INFER_PARAMETER_TYPE = DiagnosticFactory0.create(ERROR);
@@ -303,6 +303,7 @@ public class DefaultErrorMessages {
MAP.put(DELEGATION_IN_INTERFACE, "Interfaces cannot use delegation");
MAP.put(DELEGATION_NOT_TO_INTERFACE, "Only interfaces can be delegated to");
MAP.put(NO_CONSTRUCTOR, "This class does not have a constructor");
MAP.put(RESOLUTION_TO_CLASSIFIER, "{2}", NAME, TO_STRING, STRING);
MAP.put(NOT_A_CLASS, "Not a class");
MAP.put(ILLEGAL_ESCAPE_SEQUENCE, "Illegal escape sequence");
@@ -17,12 +17,15 @@
package org.jetbrains.kotlin.resolve.calls.tower
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.CandidateResolver
@@ -157,6 +160,13 @@ class NewResolutionOldInference(
}
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference)
if (candidates.isEmpty()) {
if (reportAdditionalDiagnosticIfNoCandidates(context, name, kind, scopeTower, detailedReceiver)) {
return OverloadResolutionResultsImpl.nameNotFound()
}
}
return convertToOverloadResults(candidates, tracing, context)
}
@@ -242,6 +252,32 @@ class NewResolutionOldInference(
return resolutionResultsHandler.computeResultAndReportErrors(basicCallContext, tracing, resolvedCalls)
}
// true if we found something
private fun reportAdditionalDiagnosticIfNoCandidates(
context: BasicCallResolutionContext,
name: Name,
kind: ResolutionKind<*>,
scopeTower: ImplicitScopeTower,
detailedReceiver: DetailedReceiver?
): Boolean {
val reference = context.call.calleeExpression as? KtReferenceExpression ?: return false
val errorCadidates = when (kind) {
ResolutionKind.Function -> collectErrorCandidatesForFunction(scopeTower, name, detailedReceiver)
ResolutionKind.Variable -> collectErrorCandidatesForVariable(scopeTower, name, detailedReceiver)
else -> emptyList()
}
for (candidate in errorCadidates) {
if (candidate is ErrorCandidate.Classifier) {
context.trace.record(BindingContext.REFERENCE_TARGET, reference, candidate.descriptor)
context.trace.report(Errors.RESOLUTION_TO_CLASSIFIER.on(reference, candidate.descriptor, candidate.kind, candidate.errorMessage))
return true
}
}
return false
}
private class ImplicitScopeTowerImpl(
val resolutionContext: ResolutionContext<*>,
override val dynamicScope: MemberScope,
@@ -0,0 +1,102 @@
/*
* 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.tower
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier.*
import org.jetbrains.kotlin.resolve.scopes.receivers.DetailedReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.utils.SmartList
enum class WrongResolutionToClassifier(val message: (Name) -> String) {
TYPE_PARAMETER_AS_VALUE({ "Type parameter $it cannot be used as value" }),
TYPE_PARAMETER_AS_FUNCTION({ "Type parameter $it cannot be called as function" }),
INTERFACE_AS_VALUE({ "Interface $it does not have companion object" }),
INTERFACE_AS_FUNCTION({ "Interface $it does not have constructors" }),
CLASS_AS_VALUE({ "Class $it does not have companion object" }),
OBJECT_AS_FUNCTION({ " Function 'invoke()' is not found in object $it " })
}
sealed class ErrorCandidate<out D: DeclarationDescriptor>(val descriptor: D) {
class Classifier(classifierDescriptor: ClassifierDescriptor, val kind: WrongResolutionToClassifier)
: ErrorCandidate<ClassifierDescriptor>(classifierDescriptor) {
val errorMessage = kind.message(descriptor.name)
}
}
fun collectErrorCandidatesForFunction(
scopeTower: ImplicitScopeTower,
name: Name,
explicitReceiver: DetailedReceiver?
): Collection<ErrorCandidate<*>> {
val context = ErrorCandidateContext(scopeTower, name, explicitReceiver)
context.asClassifierCall(asFunction = true)
return context.result
}
fun collectErrorCandidatesForVariable(
scopeTower: ImplicitScopeTower,
name: Name,
explicitReceiver: DetailedReceiver?
): Collection<ErrorCandidate<*>> {
val context = ErrorCandidateContext(scopeTower, name, explicitReceiver)
context.asClassifierCall(asFunction = false)
return context.result
}
private class ErrorCandidateContext(
val scopeTower: ImplicitScopeTower,
val name: Name,
val explicitReceiver: DetailedReceiver?
) {
val result = SmartList<ErrorCandidate<*>>()
fun add(errorCandidate: ErrorCandidate<*>) { result.add(errorCandidate) }
}
private fun ErrorCandidateContext.asClassifierCall(asFunction: Boolean) {
val classifier =
when (explicitReceiver) {
is ReceiverValueWithSmartCastInfo -> {
explicitReceiver.receiverValue.type.memberScope.getContributedClassifier(name, scopeTower.location)
}
is QualifierReceiver -> {
explicitReceiver.staticScope.getContributedClassifier(name, scopeTower.location)
}
else -> scopeTower.lexicalScope.findClassifier(name, scopeTower.location)
} ?: return
val kind =
when (classifier) {
is TypeParameterDescriptor -> if (asFunction) TYPE_PARAMETER_AS_FUNCTION else TYPE_PARAMETER_AS_VALUE
is ClassDescriptor -> {
when (classifier.kind) {
ClassKind.INTERFACE -> if (asFunction) INTERFACE_AS_FUNCTION else INTERFACE_AS_VALUE
ClassKind.OBJECT -> if (asFunction) OBJECT_AS_FUNCTION else return
ClassKind.CLASS -> if (asFunction) return else CLASS_AS_VALUE
else -> return
}
}
else -> return
}
add(ErrorCandidate.Classifier(classifier, kind))
}
+1 -1
View File
@@ -27,7 +27,7 @@ fun test(<!UNUSED_PARAMETER!>l<!> : <!PLATFORM_CLASS_MAPPED_TO_KOTLIN!>java.util
checkSubtype<Set<Int>?>(Collections.singleton<Int>(1))
Collections.singleton<Int>(<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1.0<!>)
<!UNRESOLVED_REFERENCE!>List<!><Int>
<!RESOLUTION_TO_CLASSIFIER!>List<!><Int>
val <!UNUSED_VARIABLE!>o<!> = "sdf" as <!PLATFORM_CLASS_MAPPED_TO_KOTLIN!>Object<!>
@@ -30,7 +30,7 @@ fun test(with: WithClassObject, without: WithoutClassObject, obj: Obj) {
with.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>.foo()
with.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedEnum<!>.A
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>
with.<!UNRESOLVED_REFERENCE!>NestedObj<!>()
with.<!RESOLUTION_TO_CLASSIFIER!>NestedObj<!>()
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>.foo()
without.<!UNRESOLVED_REFERENCE!>Nested<!>()
@@ -39,7 +39,7 @@ fun test(with: WithClassObject, without: WithoutClassObject, obj: Obj) {
without.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>.foo()
without.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedEnum<!>.A
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>
without.<!UNRESOLVED_REFERENCE!>NestedObj<!>()
without.<!RESOLUTION_TO_CLASSIFIER!>NestedObj<!>()
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>.foo()
obj.<!UNRESOLVED_REFERENCE!>Nested<!>()
@@ -48,6 +48,6 @@ fun test(with: WithClassObject, without: WithoutClassObject, obj: Obj) {
obj.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>.foo()
obj.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedEnum<!>.A
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>
obj.<!UNRESOLVED_REFERENCE!>NestedObj<!>()
obj.<!RESOLUTION_TO_CLASSIFIER!>NestedObj<!>()
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>.foo()
}
+1 -1
View File
@@ -11,6 +11,6 @@ class C {
}
fun f() {
<!UNRESOLVED_REFERENCE!>TestInterface<!>()
<!RESOLUTION_TO_CLASSIFIER!>TestInterface<!>()
C.<!UNRESOLVED_REFERENCE!>I<!>()
}
@@ -0,0 +1,6 @@
interface MutableMatrix<T> {
}
fun <T> toMutableMatrix(): MutableMatrix<T> {
return <!RESOLUTION_TO_CLASSIFIER!>MutableMatrix<!><T>()
}
@@ -0,0 +1,9 @@
package
public fun </*0*/ T> toMutableMatrix(): MutableMatrix<T>
public interface MutableMatrix</*0*/ T> {
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
}
@@ -0,0 +1,19 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
interface A
object B
class C
fun test() {
val interface_as_fun = <!RESOLUTION_TO_CLASSIFIER!>A<!>()
val interface_as_val = <!NO_COMPANION_OBJECT!>A<!>
val object_as_fun = <!FUNCTION_EXPECTED!>B<!>()
val class_as_val = <!NO_COMPANION_OBJECT!>C<!>
}
fun <T> bar() {
val typeParameter_as_val = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>
val typeParameter_as_fun = <!RESOLUTION_TO_CLASSIFIER!>T<!>()
}
@@ -0,0 +1,24 @@
package
public fun </*0*/ T> bar(): kotlin.Unit
public fun test(): 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 object B {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C {
public constructor C()
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
}
@@ -0,0 +1,39 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
object X {
interface A
object B
class C
}
fun testX() {
val interface_as_fun = X.<!RESOLUTION_TO_CLASSIFIER!>A<!>()
val interface_as_val = X.<!NO_COMPANION_OBJECT!>A<!>
val object_as_fun = X.<!FUNCTION_EXPECTED!>B<!>()
val class_as_val = X.<!NO_COMPANION_OBJECT!>C<!>
}
class Y {
interface A
object B
class C
}
fun testY() {
val interface_as_fun = Y.<!RESOLUTION_TO_CLASSIFIER!>A<!>()
val interface_as_val = Y.<!NO_COMPANION_OBJECT!>A<!>
val object_as_fun = Y.<!FUNCTION_EXPECTED!>B<!>()
val class_as_val = Y.<!NO_COMPANION_OBJECT!>C<!>
}
fun test(x: X) {
val interface_as_fun = x.<!RESOLUTION_TO_CLASSIFIER!>A<!>()
val interface_as_val = x.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>A<!>
val object_as_fun = x.<!RESOLUTION_TO_CLASSIFIER!>B<!>()
val class_as_val = x.<!NO_COMPANION_OBJECT, NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>C<!>
}
@@ -0,0 +1,59 @@
package
public fun test(/*0*/ x: X): kotlin.Unit
public fun testX(): kotlin.Unit
public fun testY(): kotlin.Unit
public object X {
private constructor X()
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 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 object B {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public final class Y {
public constructor Y()
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 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 object B {
private constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C {
public constructor C()
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
}
}
@@ -18,7 +18,7 @@ object AnObject
typealias TO = AnObject
val test6 = <!UNRESOLVED_REFERENCE!>TI<!>()
val test6a = <!UNRESOLVED_REFERENCE!>Interface<!>()
val test6a = <!RESOLUTION_TO_CLASSIFIER!>Interface<!>()
val test7 = <!FUNCTION_EXPECTED!>TO<!>()
val test7a = <!FUNCTION_EXPECTED!>AnObject<!>()
@@ -15603,6 +15603,33 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
}
}
@TestMetadata("compiler/testData/diagnostics/tests/resolve/noCandidates")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class NoCandidates extends AbstractDiagnosticsTest {
public void testAllFilesPresentInNoCandidates() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/resolve/noCandidates"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("kt2787.kt")
public void testKt2787() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/noCandidates/kt2787.kt");
doTest(fileName);
}
@TestMetadata("resolvedToClassifier.kt")
public void testResolvedToClassifier() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.kt");
doTest(fileName);
}
@TestMetadata("resolvedToClassifierWithReceiver.kt")
public void testResolvedToClassifierWithReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifierWithReceiver.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/resolve/overloadConflicts")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -52,10 +52,10 @@ Exit code: ABORT
------------------------------------------
COMPILATION FAILED
This class does not have a constructor
Unresolved reference: A
Unresolved reference: A
Unresolved reference: A
Unresolved reference: A
Interface A does not have constructors
Interface A does not have constructors
Interface A does not have constructors
Interface A does not have constructors
================ Step #2 =================
@@ -23,5 +23,5 @@ End of files
Exit code: ABORT
------------------------------------------
COMPILATION FAILED
Unresolved reference: SamInterface
Unresolved reference: SamInterface
Interface SamInterface does not have constructors
Interface SamInterface does not have constructors