Prefer package with class files to class with the same name
See comments in JvmDependenciesIndexImpl and in the test #KT-15464 Fixed
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.index
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.IntArrayList
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
@@ -236,15 +239,19 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>): JvmDependenciesIndex {
|
||||
private fun VirtualFile.findChildPackage(subPackageName: String, rootType: JavaRoot.RootType): VirtualFile? {
|
||||
val childDirectory = findChild(subPackageName) ?: return null
|
||||
|
||||
// Resolve conflicts between files and packages with the same qualified name in favor of files
|
||||
val fileExtension = when (rootType) {
|
||||
JavaRoot.RootType.BINARY -> ".class"
|
||||
JavaRoot.RootType.SOURCE -> ".java"
|
||||
JavaRoot.RootType.BINARY -> JavaClassFileType.INSTANCE.defaultExtension
|
||||
JavaRoot.RootType.SOURCE -> JavaFileType.INSTANCE.defaultExtension
|
||||
}
|
||||
|
||||
if (findChild(subPackageName + fileExtension)?.isDirectory == false) {
|
||||
return null
|
||||
// If in addition to a directory "foo" there's a class file "foo.class" AND there are no classes anywhere in the directory "foo",
|
||||
// then we ignore the directory and let the resolution choose the class "foo" instead.
|
||||
if (findChild("$subPackageName.$fileExtension")?.isDirectory == false) {
|
||||
if (VfsUtilCore.processFilesRecursively(childDirectory) { file -> file.extension != fileExtension }) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return childDirectory
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test;
|
||||
|
||||
public class Boo {
|
||||
public interface Nested {}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package test;
|
||||
|
||||
public class Foo {
|
||||
public interface Nested {
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test.Boo.SubBoo;
|
||||
|
||||
public class C {
|
||||
public interface Nested {}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package test.Foo;
|
||||
|
||||
public class Bar {
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
compiler/testData/compileKotlinAgainstCustomBinaries/innerClassPackageConflict2/source.kt:10:9: error: unresolved reference: Nested
|
||||
val v3: Nested? = null
|
||||
^
|
||||
compiler/testData/compileKotlinAgainstCustomBinaries/innerClassPackageConflict2/source.kt:11:18: error: unresolved reference: Nested
|
||||
val v4: test.Foo.Nested? = null
|
||||
^
|
||||
compiler/testData/compileKotlinAgainstCustomBinaries/innerClassPackageConflict2/source.kt:14:18: error: unresolved reference: Nested
|
||||
val v6: test.Boo.Nested? = null
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import test.Foo.*
|
||||
|
||||
// Note that unlike in Java, in Kotlin we currently mostly prefer package to class in qualified name resolution.
|
||||
// So here, for example, we see both the package and the class with the name test.Foo, and prefer the former.
|
||||
// So 'Bar' should be resolved, 'Nested' should be unresolved.
|
||||
// For javac, the opposite is true: 'Bar' would be unresolved in a similar situation, 'Nested' would be resolved.
|
||||
|
||||
val v1: Bar? = null
|
||||
val v2: test.Foo.Bar? = null
|
||||
val v3: Nested? = null
|
||||
val v4: test.Foo.Nested? = null
|
||||
|
||||
val v5: test.Boo.SubBoo.C.Nested? = null
|
||||
val v6: test.Boo.Nested? = null
|
||||
+24
@@ -20,6 +20,7 @@ import com.google.common.collect.Iterables;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import kotlin.Pair;
|
||||
import kotlin.collections.SetsKt;
|
||||
import kotlin.io.FilesKt;
|
||||
@@ -565,4 +566,27 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
|
||||
Pair<String, ExitCode> output = compileKotlin("source.kt", tmpdir, jarPath);
|
||||
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
||||
}
|
||||
|
||||
public void testInnerClassPackageConflict2() throws Exception {
|
||||
final File library1 = compileJava("library1");
|
||||
final File library2 = compileJava("library2");
|
||||
|
||||
// Copy everything from library2 to library1
|
||||
FileUtil.visitFiles(library2, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (!file.isDirectory()) {
|
||||
File newFile = new File(library1, FilesKt.relativeTo(file, library2).getPath());
|
||||
if (!newFile.getParentFile().exists()) {
|
||||
assert newFile.getParentFile().mkdirs();
|
||||
}
|
||||
assert file.renameTo(newFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Pair<String, ExitCode> output = compileKotlin("source.kt", tmpdir, library1);
|
||||
KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user