Run classifier usage checkers for ambiguous references when possible

See comment in getReferencedClassifier and importJavaSamInterface.kt for
explanation
This commit is contained in:
Alexander Udalov
2016-09-24 16:54:55 +03:00
parent ec53efd869
commit 1e51ba7d00
3 changed files with 36 additions and 2 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtReferenceExpression
@@ -42,7 +43,7 @@ interface ClassifierUsageChecker {
return
}
val target = trace.bindingContext.get(BindingContext.REFERENCE_TARGET, expression) as? ClassifierDescriptor ?: return
val target = getReferencedClassifier(expression) ?: return
for (checker in checkers) {
checker.check(target, trace, expression)
@@ -56,12 +57,26 @@ interface ClassifierUsageChecker {
}
}
private fun getReferencedClassifier(expression: KtReferenceExpression): ClassifierDescriptor? {
val target = trace.get(BindingContext.REFERENCE_TARGET, expression) as? ClassifierDescriptor
if (target != null) return target
// "Comparable" in "import java.lang.Comparable" references both a class and a SAM constructor and prevents
// REFERENCE_TARGET from being recorded in favor of AMBIGUOUS_REFERENCE_TARGET. But we must still run checkers
// to report if there's something wrong with the class. We characterize this case below by the following properties:
// 1) Exactly one of the references is a classifier
// 2) All references refer to the same source element, i.e. their source is the same
val targets = trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, expression) ?: return null
if (targets.groupBy { (it as? DeclarationDescriptorWithSource)?.source }.size != 1) return null
return targets.filterIsInstance<ClassifierDescriptor>().singleOrNull()
}
/**
* @return true iff [expression] references the companion of a class Foo via the name Foo, e.g. in `Foo.bar()`
*/
private fun isReferenceToCompanionViaOuterClass(expression: KtReferenceExpression, target: ClassifierDescriptor?): Boolean {
return DescriptorUtils.isCompanionObject(target) &&
trace.bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression] != null
trace.get(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression) != null
}
}
@@ -0,0 +1,13 @@
// SKIP_TXT
// FILE: test/J.java
package test;
@Deprecated
public interface J {
public String foo(int x);
}
// FILE: K.kt
import test.<!DEPRECATION!>J<!>
@@ -5946,6 +5946,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("importJavaSamInterface.kt")
public void testImportJavaSamInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/importJavaSamInterface.kt");
doTest(fileName);
}
@TestMetadata("imports.kt")
public void testImports() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/deprecated/imports.kt");