Report diagnostic on import with explicit import class if class was imported earlier.

This commit is contained in:
Stanislav Erokhin
2015-09-24 14:07:43 +03:00
parent 6c1738e11a
commit fc9aa87a05
18 changed files with 215 additions and 104 deletions
@@ -105,7 +105,7 @@ public interface Errors {
DiagnosticFactory1<JetSimpleNameExpression, Name> CANNOT_BE_IMPORTED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<JetSimpleNameExpression> PACKAGE_CANNOT_BE_IMPORTED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<JetExpression, String> CONFLICTING_IMPORT = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetImportDirective, String> CONFLICTING_IMPORT = DiagnosticFactory1.create(ERROR, PositioningStrategies.IMPORT_ALIAS);
// Modifiers
@@ -452,4 +452,18 @@ public object PositioningStrategies {
return listOf(TextRange(element.getOperationReference().startOffset, element.endOffset))
}
}
public val IMPORT_ALIAS: PositioningStrategy<JetImportDirective> = object: PositioningStrategy<JetImportDirective>() {
override fun mark(element: JetImportDirective): List<TextRange> {
element.aliasNameNode?.let { return markNode(it) }
element.importedReference?.let {
if (it is JetQualifiedExpression) {
it.selectorExpression?.let { return markElement(it) }
}
return markElement(it)
}
return markElement(element)
}
}
}
@@ -300,6 +300,9 @@ public class JetPsiUtil {
@Nullable
public static Name getAliasName(@NotNull JetImportDirective importDirective) {
if (importDirective.isAllUnder()) {
return null;
}
String aliasName = importDirective.getAliasName();
JetExpression importedReference = importDirective.getImportedReference();
if (importedReference == null) {
@@ -118,14 +118,14 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
moduleDescriptor: ModuleDescriptor,
trace: BindingTrace,
packageFragmentForVisibilityCheck: PackageFragmentDescriptor?
): JetScope {
val importedReference = importDirective.importedReference ?: return JetScope.Empty
): JetScope? { // null if some error happened
val importedReference = importDirective.importedReference ?: return null
val path = importedReference.asQualifierPartList(trace)
val lastPart = path.lastOrNull() ?: return JetScope.Empty
val lastPart = path.lastOrNull() ?: return null
if (importDirective.isAllUnder) {
val packageOrClassDescriptor = resolveToPackageOrClass(path, moduleDescriptor, trace, packageFragmentForVisibilityCheck,
scopeForFirstPart = null, inImport = true) ?: return JetScope.Empty
scopeForFirstPart = null, inImport = true) ?: return null
if (packageOrClassDescriptor is ClassDescriptor && packageOrClassDescriptor.kind.isSingleton) {
trace.report(Errors.CANNOT_IMPORT_MEMBERS_FROM_SINGLETON.on(lastPart.expression, packageOrClassDescriptor)) // todo report on star
}
@@ -135,12 +135,12 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
val aliasName = JetPsiUtil.getAliasName(importDirective)
if (aliasName == null) { // import kotlin.
resolveToPackageOrClass(path, moduleDescriptor, trace, packageFragmentForVisibilityCheck, scopeForFirstPart = null, inImport = true)
return JetScope.Empty
return null
}
val packageOrClassDescriptor = resolveToPackageOrClass(path.subList(0, path.size() - 1), moduleDescriptor,
trace, packageFragmentForVisibilityCheck, scopeForFirstPart = null, inImport = true)
?: return JetScope.Empty
?: return null
val descriptors = SmartList<DeclarationDescriptor>()
val lastName = lastPart.name
@@ -169,6 +169,7 @@ public class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageVa
}
else {
tryResolveDescriptorsWhichCannotBeImported(trace, moduleDescriptor, packageOrClassDescriptor, lastPart)
return null
}
val importedDescriptors = descriptors.filter { isVisible(it, packageFragmentForVisibilityCheck, inImport = true) }.
@@ -61,7 +61,7 @@ public class FileScopeProviderImpl(
.sure { "Could not find fragment ${file.getPackageFqName()} for file ${file.getName()}" }
fun createImportResolver(indexedImports: IndexedImports, trace: BindingTrace)
= LazyImportResolver(storageManager, qualifiedExpressionResolver, this, moduleDescriptor, indexedImports, trace, packageFragment)
= LazyImportResolver(storageManager, qualifiedExpressionResolver, moduleDescriptor, indexedImports, trace, packageFragment)
val aliasImportResolver = createImportResolver(AliasImportsIndexed(imports), bindingTrace)
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace)
@@ -16,25 +16,27 @@
package org.jetbrains.kotlin.resolve.lazy
import com.google.common.collect.HashMultimap
import com.google.common.collect.ImmutableListMultimap
import com.google.common.collect.ListMultimap
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.JetImportDirective
import org.jetbrains.kotlin.psi.JetPsiUtil
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.PlatformTypesMappedToKotlinChecker
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.util.collectionUtils.concat
import org.jetbrains.kotlin.utils.Printer
import java.util.LinkedHashSet
import java.util.*
interface IndexedImports {
val imports: List<JetImportDirective>
@@ -67,111 +69,57 @@ class AliasImportsIndexed(allImports: Collection<JetImportDirective>) : IndexedI
class LazyImportResolver(
val storageManager: StorageManager,
val qualifiedExpressionResolver: QualifiedExpressionResolver,
val fileScopeProvider: FileScopeProvider,
val moduleDescriptor: ModuleDescriptor,
val indexedImports: IndexedImports,
private val traceForImportResolve: BindingTrace,
private val packageFragment: PackageFragmentDescriptor
) {
private val importedScopesProvider = storageManager.createMemoizedFunction {
directive: JetImportDirective -> ImportDirectiveResolveCache(directive)
}
private val importedScopesProvider = storageManager.createMemoizedFunctionWithNullableValues {
directive: JetImportDirective ->
val directiveImportScope = qualifiedExpressionResolver.processImportReference(
directive, moduleDescriptor, traceForImportResolve, packageFragment) ?: return@createMemoizedFunctionWithNullableValues null
private var directiveUnderResolve: JetImportDirective? = null
private class ImportResolveStatus(val scope: JetScope, val descriptors: Collection<DeclarationDescriptor>)
private inner class ImportDirectiveResolveCache(private val directive: JetImportDirective) {
@Volatile var importResolveStatus: ImportResolveStatus? = null
fun scopeForMode(): JetScope {
val status = importResolveStatus
if (status != null) {
return status.scope
if (!directive.isAllUnder) {
PlatformTypesMappedToKotlinChecker.checkPlatformTypesMappedToKotlin(
moduleDescriptor, traceForImportResolve, directive, directiveImportScope.getAllDescriptors())
}
return storageManager.compute {
val cachedStatus = importResolveStatus
if (cachedStatus != null) {
cachedStatus.scope
}
else {
directiveUnderResolve = directive
try {
val directiveImportScope = qualifiedExpressionResolver.processImportReference(
directive, moduleDescriptor, traceForImportResolve, packageFragment)
val descriptors = if (directive.isAllUnder()) emptyList() else directiveImportScope.getAllDescriptors()
PlatformTypesMappedToKotlinChecker.checkPlatformTypesMappedToKotlin(moduleDescriptor, traceForImportResolve, directive, descriptors)
importResolveStatus = ImportResolveStatus(directiveImportScope, descriptors)
directiveImportScope
}
finally {
directiveUnderResolve = null
}
}
}
}
directiveImportScope
}
public fun forceResolveAllContents() {
val explicitClassImports = HashMultimap.create<String, JetImportDirective>()
for (importDirective in indexedImports.imports) {
forceResolveImportDirective(importDirective)
val scope = importedScopesProvider(importDirective)
val alias = JetPsiUtil.getAliasName(importDirective)?.identifier
if (scope != null && alias != null) {
if (scope.getClassifier(Name.identifier(alias), KotlinLookupLocation(importDirective)) != null) {
explicitClassImports.put(alias, importDirective)
}
}
}
for (alias in explicitClassImports.keySet()) {
val imports = explicitClassImports.get(alias)
if (imports.size() > 1) {
imports.forEach {
traceForImportResolve.report(Errors.CONFLICTING_IMPORT.on(it, alias))
}
}
}
}
public fun forceResolveImportDirective(importDirective: JetImportDirective) {
getImportScope(importDirective)
val status = importedScopesProvider(importDirective).importResolveStatus
if (status != null && !status.descriptors.isEmpty()) {
val fileScope = fileScopeProvider.getFileScope(importDirective.getContainingJetFile())
reportConflictingImport(importDirective, fileScope, status.descriptors, traceForImportResolve)
}
}
private fun reportConflictingImport(
importDirective: JetImportDirective,
fileScope: JetScope,
resolvedTo: Collection<DeclarationDescriptor>?,
trace: BindingTrace
) {
val importedReference = importDirective.getImportedReference()
if (importedReference == null || resolvedTo == null) return
val aliasName = JetPsiUtil.getAliasName(importDirective) ?: return
if (resolvedTo.size() != 1) return
when (resolvedTo.single()) {
is ClassDescriptor -> {
if (fileScope.getClassifier(aliasName) == null) {
trace.report(Errors.CONFLICTING_IMPORT.on(importedReference, aliasName.asString()))
}
}
is PackageViewDescriptor -> {
if (fileScope.getPackage(aliasName) == null) {
trace.report(Errors.CONFLICTING_IMPORT.on(importedReference, aliasName.asString()))
}
}
}
}
public fun <D : DeclarationDescriptor> selectSingleFromImports(
name: Name,
descriptorSelector: (JetScope, Name) -> D?
): D? {
fun compute(): D? {
val imports = indexedImports.importsForName(name)
if (imports.contains(directiveUnderResolve)) {
// This is the recursion in imports analysis
return null
}
var target: D? = null
for (directive in imports) {
@@ -191,11 +139,6 @@ class LazyImportResolver(
return storageManager.compute {
var descriptors: Collection<D>? = null
for (directive in indexedImports.importsForName(name)) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw IllegalStateException("Recursion while resolving many imports: " + directive.getText())
}
val descriptorsForImport = descriptorsSelector(getImportScope(directive), name)
descriptors = descriptors.concat(descriptorsForImport)
}
@@ -205,7 +148,7 @@ class LazyImportResolver(
}
public fun getImportScope(directive: JetImportDirective): JetScope {
return importedScopesProvider(directive).scopeForMode()
return importedScopesProvider(directive) ?: JetScope.Empty
}
}
@@ -1,8 +1,8 @@
//FILE:a.kt
package a
import <!CONFLICTING_IMPORT!>b.O<!>
import <!CONFLICTING_IMPORT!>c.O<!>
import b.<!CONFLICTING_IMPORT!>O<!>
import c.<!CONFLICTING_IMPORT!>O<!>
//FILE:b.kt
package b
@@ -12,4 +12,4 @@ object O {}
//FILE:c.kt
package c
object O {}
object O {}
@@ -11,7 +11,7 @@ class X
// FILE: c.kt
package c
import <!CONFLICTING_IMPORT!>a.X<!>
import <!CONFLICTING_IMPORT!>b.X<!>
import a.<!CONFLICTING_IMPORT!>X<!>
import b.<!CONFLICTING_IMPORT!>X<!>
class Y : <!UNRESOLVED_REFERENCE!>X<!>
@@ -0,0 +1,48 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: 1.kt
package a
class someFun() {}
fun someFun(i: Int) {}
class someVal() {}
val Int.someVal: Int get() = 3
class A
class B
// FILE: 2.kt
package b
class someFun
class someVal
class someAll
fun A() {}
class B
// FILE: 3.kt
import a.<!CONFLICTING_IMPORT!>someFun<!>
import b.<!CONFLICTING_IMPORT!>someFun<!>
import a.<!CONFLICTING_IMPORT!>someVal<!>
import b.<!CONFLICTING_IMPORT!>someVal<!>
import a.A
import b.A
// FILE: 4.kt
import b.*
import a.B
// FILE: 5.kt
package b
import a.B
// FILE: 6.kt
import a.<!CONFLICTING_IMPORT!>B<!>
import b.<!CONFLICTING_IMPORT!>B<!>
@@ -0,0 +1,66 @@
package
package a {
public val kotlin.Int.someVal: kotlin.Int
public fun someFun(/*0*/ i: kotlin.Int): kotlin.Unit
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 class B {
public 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 someFun {
public constructor someFun()
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 someVal {
public constructor someVal()
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
}
}
package b {
public fun A(): kotlin.Unit
public final class B {
public 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 someAll {
public constructor someAll()
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 someFun {
public constructor someFun()
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 someVal {
public constructor someVal()
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
}
}
@@ -14,8 +14,8 @@ class D {
}
// FILE: c.kt
import <!CONFLICTING_IMPORT!>a.A.B<!>
import <!CONFLICTING_IMPORT!>a.D.B<!>
import a.A.<!CONFLICTING_IMPORT!>B<!>
import a.D.<!CONFLICTING_IMPORT!>B<!>
fun test(b: <!UNRESOLVED_REFERENCE!>B<!>) {
<!UNRESOLVED_REFERENCE!>B<!>()
@@ -6603,6 +6603,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("ImportClassClash.kt")
public void testImportClassClash() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/imports/ImportClassClash.kt");
doTest(fileName);
}
@TestMetadata("ImportFromCurrentWithDifferentName.kt")
public void testImportFromCurrentWithDifferentName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/imports/ImportFromCurrentWithDifferentName.kt");
@@ -82,7 +82,7 @@ public fun ResolutionFacade.resolveImportReference(
val importDirective = JetPsiFactory(project).createImportDirective(ImportPath(fqName, false))
val qualifiedExpressionResolver = this.getFrontendService(moduleDescriptor, QualifiedExpressionResolver::class.java)
return qualifiedExpressionResolver.processImportReference(
importDirective, moduleDescriptor, BindingTraceContext(), packageFragmentForVisibilityCheck = null).getAllDescriptors()
importDirective, moduleDescriptor, BindingTraceContext(), packageFragmentForVisibilityCheck = null)?.getAllDescriptors() ?: emptyList()
}
//NOTE: idea default API returns module search scope for file under module but not in source or production source (for example, test data )
@@ -0,0 +1,5 @@
// "Remove conflicting import for 'java.util.ArrayList'" "true"
package test
import java.util.ArrayList
import java.util.ArrayList<caret>
@@ -0,0 +1,4 @@
// "Remove conflicting import for 'java.util.ArrayList'" "true"
package test
import java.util.ArrayList
@@ -0,0 +1,5 @@
// "Remove conflicting import for 'java.util.ArrayList'" "true"
package test
import java.util.ArrayList as foo
import java.util.ArrayList as foo<caret>
@@ -0,0 +1,4 @@
// "Remove conflicting import for 'java.util.ArrayList'" "true"
package test
import java.util.ArrayList as foo
@@ -723,6 +723,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/conflictingImports/removeConflictingImport.kt");
doTest(fileName);
}
@TestMetadata("removeDuplicateImport.kt")
public void testRemoveDuplicateImport() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/conflictingImports/removeDuplicateImport.kt");
doTest(fileName);
}
@TestMetadata("removeDuplicateImportWithAlias.kt")
public void testRemoveDuplicateImportWithAlias() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/conflictingImports/removeDuplicateImportWithAlias.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage")