diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt index 9f5c337a405..3eb47e8a7f6 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt @@ -44,9 +44,11 @@ internal class ClasspathRootsResolver( private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile? ) { private val javaModuleFinder = CliJavaModuleFinder(VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)) - private val javaModuleGraph = JavaModuleGraph(javaModuleFinder) + val javaModuleGraph = JavaModuleGraph(javaModuleFinder) - fun convertClasspathRoots(contentRoots: Iterable): List { + data class RootsAndModules(val roots: List, val modules: List) + + fun convertClasspathRoots(contentRoots: Iterable): RootsAndModules { val result = mutableListOf() val modules = ArrayList() @@ -79,7 +81,7 @@ internal class ClasspathRootsResolver( addModularRoots(modules, result) - return result + return RootsAndModules(result, modules) } private fun modularSourceRoot(root: VirtualFile): JavaModule.Explicit? { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 415344e375f..43a6b68eaa7 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -78,6 +78,7 @@ import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker import org.jetbrains.kotlin.cli.jvm.config.* import org.jetbrains.kotlin.cli.jvm.index.* import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar +import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension @@ -98,6 +99,7 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider @@ -194,7 +196,9 @@ class KotlinCoreEnvironment private constructor( classpathRootsResolver = ClasspathRootsResolver(PsiManager.getInstance(project), messageCollector, this::contentRootToVirtualFile) - initialRoots = classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS)) + val (initialRoots, javaModules) = + classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS)) + this.initialRoots = initialRoots if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) { JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency( @@ -219,6 +223,11 @@ class KotlinCoreEnvironment private constructor( configuration.getBoolean(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING) ) + project.registerService( + JavaModuleResolver::class.java, + CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules) + ) + val finderFactory = CliVirtualFileFinderFactory(rootsIndex) project.registerService(MetadataFinderFactory::class.java, finderFactory) project.registerService(VirtualFileFinderFactory::class.java, finderFactory) @@ -277,8 +286,9 @@ class KotlinCoreEnvironment private constructor( } } - internal fun updateClasspath(contentRoots: List): List? { - val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots) + fun updateClasspath(contentRoots: List): List? { + // TODO: add new Java modules to CliJavaModuleResolver + val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots).roots for (packagePartProvider in packagePartProviders) { packagePartProvider.addRoots(newRoots) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt new file mode 100644 index 00000000000..8bb5221a6c1 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleResolver.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2017 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.cli.jvm.modules + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleGraph +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver + +class CliJavaModuleResolver( + override val moduleGraph: JavaModuleGraph, + private val javaModules: List +) : JavaModuleResolver { + override fun findJavaModule(file: VirtualFile): JavaModule? { + val isBinary = file.fileType == JavaClassFileType.INSTANCE + return javaModules.firstOrNull { module -> + module.isBinary == isBinary && VfsUtilCore.isAncestor(module.moduleRoot, file, false) + } + } +} diff --git a/compiler/container/src/org/jetbrains/kotlin/container/Cache.kt b/compiler/container/src/org/jetbrains/kotlin/container/Cache.kt index 978eb36c077..a04dc039133 100644 --- a/compiler/container/src/org/jetbrains/kotlin/container/Cache.kt +++ b/compiler/container/src/org/jetbrains/kotlin/container/Cache.kt @@ -18,8 +18,7 @@ package org.jetbrains.kotlin.container import com.intellij.util.containers.ContainerUtil import java.lang.reflect.* -import java.util.ArrayList -import java.util.LinkedHashSet +import java.util.* private object ClassTraversalCache { private val cache = ContainerUtil.createConcurrentWeakKeySoftValueMap, ClassInfo>() @@ -82,7 +81,11 @@ private fun getConstructorInfo(c: Class<*>): ConstructorInfo? { return null val constructor = constructors.single() - return ConstructorInfo(constructor, constructor.genericParameterTypes.toList()) + val parameterTypes = + if (c.declaringClass != null && !Modifier.isStatic(c.modifiers)) + listOf(c.declaringClass, *constructor.genericParameterTypes) + else constructor.genericParameterTypes.toList() + return ConstructorInfo(constructor, parameterTypes) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt new file mode 100644 index 00000000000..3980368680f --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmModuleAccessibilityChecker.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2010-2017 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.jvm.checkers + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker +import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.checkers.ClassifierUsageChecker +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor + +class JvmModuleAccessibilityChecker(project: Project) : CallChecker { + private val moduleResolver = JavaModuleResolver.getInstance(project) + + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + val descriptor = resolvedCall.resultingDescriptor + + // javac seems to check only the containing class of the member being called. Note that it's fine to call, for example, + // members with parameter types or return type from an unexported package + val targetDescriptor = DescriptorUtils.getParentOfType(descriptor, ClassOrPackageFragmentDescriptor::class.java) ?: return + + val fileFromOurModule = DescriptorToSourceUtils.getContainingFile(context.scope.ownerDescriptor)?.virtualFile + diagnosticFor(targetDescriptor, descriptor, fileFromOurModule, reportOn)?.let(context.trace::report) + } + + private fun diagnosticFor( + targetClassOrPackage: ClassOrPackageFragmentDescriptor, + originalDescriptor: DeclarationDescriptorWithSource?, + fileFromOurModule: VirtualFile?, + reportOn: PsiElement + ): Diagnostic? { + val referencedFile = findVirtualFile(targetClassOrPackage, originalDescriptor) ?: return null + + // TODO: do not resolve our JavaModule every time, invent a way to obtain it from ModuleDescriptor and do it once in constructor + val ourModule = fileFromOurModule?.let(moduleResolver::findJavaModule) + val theirModule = moduleResolver.findJavaModule(referencedFile) + + // If we're both in the unnamed module, it's OK, no error should be reported + if (ourModule == null && theirModule == null) return null + + if (theirModule == null) { + // We should probably prohibit this usage according to JPMS (named module cannot use types from unnamed module), + // but we cannot be sure that a module without module-info.java is going to be actually used as an unnamed module. + // It could also be an automatic module, in which case it would be read by every module. + return null + } + + val containingPackage = DescriptorUtils.getParentOfType(targetClassOrPackage, PackageFragmentDescriptor::class.java, false) + val fqName = containingPackage?.fqNameUnsafe?.toSafe() ?: return null + if (theirModule.exports(fqName)) return null + + if (ourModule != null) { + if (ourModule.name == theirModule.name) return null + + if (!moduleResolver.moduleGraph.reads(ourModule.name, theirModule.name)) { + return ErrorsJvm.JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE.on(reportOn, theirModule.name) + } + + if (theirModule.exportsTo(fqName, ourModule.name)) return null + } + + return ErrorsJvm.JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE.on(reportOn, theirModule.name, fqName.asString()) + } + + private fun findVirtualFile( + descriptor: ClassOrPackageFragmentDescriptor, + originalDescriptor: DeclarationDescriptorWithSource? + ): VirtualFile? { + val source = descriptor.source + when (source) { + is KotlinJvmBinarySourceElement -> (source.binaryClass as? VirtualFileKotlinClass)?.file?.let { return it } + is JavaSourceElement -> (source.javaElement as? VirtualFileBoundJavaClass)?.virtualFile?.let { return it } + is KotlinJvmBinaryPackageSourceElement -> { + if (originalDescriptor is DeserializedMemberDescriptor) { + (source.getContainingBinaryClass(originalDescriptor) as? VirtualFileKotlinClass)?.file?.let { return it } + } + } + } + + source.getPsi()?.containingFile?.virtualFile?.let { return it } + + return originalDescriptor?.source?.getPsi()?.containingFile?.virtualFile + } + + inner class ClassifierUsage : ClassifierUsageChecker { + override fun check( + targetDescriptor: ClassifierDescriptor, + trace: BindingTrace, + element: PsiElement, + languageVersionSettings: LanguageVersionSettings + ) { + val targetClassOrPackage = when (targetDescriptor) { + is ClassDescriptor -> targetDescriptor + is TypeAliasDescriptor -> { + DescriptorUtils.getParentOfType(targetDescriptor, ClassOrPackageFragmentDescriptor::class.java) ?: return + } + else -> return + } + + diagnosticFor(targetClassOrPackage, targetDescriptor, element.containingFile.virtualFile, element)?.let(trace::report) + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 5ce975a3ab9..fcf105aba40 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -112,7 +112,10 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET, "Calls to static methods in Java interfaces are deprecated in JVM target 1.6. Recompile with '-jvm-target 1.8'"); MAP.put(INLINE_FROM_HIGHER_PLATFORM, "Cannot inline bytecode built with {0} into bytecode that is being built with {1}. Please specify proper ''-jvm-target'' option", STRING, STRING); - MAP.put(ErrorsJvm.OBSOLETE_SUSPEND_INLINE_FUNCTIONS_ABI, "Cannot inline suspend function built with compiler version less than 1.1.4/1.2-M1"); + MAP.put(OBSOLETE_SUSPEND_INLINE_FUNCTIONS_ABI, "Cannot inline suspend function built with compiler version less than 1.1.4/1.2-M1"); + + MAP.put(JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE, "Symbol is declared in module ''{0}'' which current module does not depend on", STRING); + MAP.put(JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE, "Symbol is declared in module ''{0}'' which does not export package ''{1}''", STRING, STRING); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index cc15cc7100e..d55c1ec9999 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -95,6 +95,9 @@ public interface ErrorsJvm { DiagnosticFactory2 INLINE_FROM_HIGHER_PLATFORM = DiagnosticFactory2.create(ERROR); DiagnosticFactory0 OBSOLETE_SUSPEND_INLINE_FUNCTIONS_ABI = DiagnosticFactory0.create(ERROR); + DiagnosticFactory1 JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE = DiagnosticFactory1.create(ERROR); + DiagnosticFactory2 JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE = DiagnosticFactory2.create(ERROR); + @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleGraph.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleGraph.kt index 2859bb71463..d0d166c15dc 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleGraph.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleGraph.kt @@ -29,6 +29,7 @@ class JavaModuleGraph(finder: JavaModuleFinder) { visited += "java.base" fun dfs(moduleName: String) { + // Automatic modules have no transitive exports, so we only consider explicit modules here val moduleInfo = (module(moduleName) as? JavaModule.Explicit)?.moduleInfo ?: return for ((dependencyModuleName, isTransitive) in moduleInfo.requires) { if (!visited.add(dependencyModuleName) && isTransitive) { @@ -40,4 +41,30 @@ class JavaModuleGraph(finder: JavaModuleFinder) { moduleNames.forEach(::dfs) return visited.toList() } + + fun reads(moduleName: String, dependencyName: String): Boolean { + if (moduleName == dependencyName || dependencyName == "java.base") return true + + val visited = linkedSetOf() + + fun dfs(name: String): Boolean { + if (!visited.add(name)) return false + + val module = module(name) + when (module) { + is JavaModule.Automatic -> return true + is JavaModule.Explicit -> { + for ((dependencyModuleName, isTransitive) in module.moduleInfo.requires) { + if (dependencyModuleName == dependencyName) return true + if (isTransitive && dfs(dependencyName)) return true + } + return false + } + null -> return false + else -> error("Unsupported module type: $module") + } + } + + return dfs(moduleName) + } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleResolver.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleResolver.kt new file mode 100644 index 00000000000..d4d9f9d505b --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleResolver.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2017 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.jvm.modules + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +interface JavaModuleResolver { + val moduleGraph: JavaModuleGraph + + // For any .java, .kt or .class file in the project, returns the corresponding module or null if there's none + fun findJavaModule(file: VirtualFile): JavaModule? + + companion object SERVICE { + fun getInstance(project: Project): JavaModuleResolver = + ServiceManager.getService(project, JavaModuleResolver::class.java) + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 0b607aef48b..91bb1fead10 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -86,6 +86,8 @@ object JvmPlatformConfigurator : PlatformConfigurator( container.useImpl() container.useImpl() container.useImpl() + container.useImpl() + container.useImpl() container.useInstance(JvmTypeSpecificityComparator) } } diff --git a/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java b/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java index 37bd31ea8da..a6e485b8c2d 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java +++ b/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java @@ -501,7 +501,7 @@ public class KotlinTestUtils { JvmContentRootsKt.addJvmClasspathRoots(configuration, PathUtil.getJdkClassesRootsFromJre(getJreHome(jdk6))); } else if (jdkKind == TestJdkKind.FULL_JDK_9) { - File home = getJre9HomeIfPossible(); + File home = getJdk9HomeIfPossible(); if (home != null) { configuration.put(JVMConfigurationKeys.JDK_HOME, home); } @@ -529,8 +529,11 @@ public class KotlinTestUtils { } @Nullable - public static File getJre9HomeIfPossible() { + public static File getJdk9HomeIfPossible() { String jdk9 = System.getenv("JDK_19"); + if (jdk9 == null) { + jdk9 = System.getenv("JDK_9"); + } if (jdk9 == null) { // TODO: replace this with a failure as soon as Java 9 is installed on all TeamCity agents System.err.println("Environment variable JDK_19 is not set, the test will be skipped"); diff --git a/compiler/tests-common/org/jetbrains/kotlin/test/MockLibraryUtil.kt b/compiler/tests-common/org/jetbrains/kotlin/test/MockLibraryUtil.kt index 78522bf267f..d97d9e5f1a8 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/test/MockLibraryUtil.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/test/MockLibraryUtil.kt @@ -46,11 +46,11 @@ object MockLibraryUtil { jarName: String, addSources: Boolean = false, extraOptions: List = emptyList(), - extraClasspath: List = emptyList() + extraClasspath: List = emptyList(), + useJava9: Boolean = false ): File { - return compileLibraryToJar( - sourcesPath, KotlinTestUtils.tmpDir("testLibrary-" + jarName), jarName, addSources, extraOptions, extraClasspath - ) + return compileLibraryToJar(sourcesPath, KotlinTestUtils.tmpDir("testLibrary-" + jarName), jarName, addSources, + extraOptions, extraClasspath, useJava9) } @JvmStatic @@ -61,7 +61,8 @@ object MockLibraryUtil { jarName: String, addSources: Boolean = false, extraOptions: List = emptyList(), - extraClasspath: List = emptyList() + extraClasspath: List = emptyList(), + useJava9: Boolean = false ): File { val classesDir = File(contentDir, "classes") @@ -91,7 +92,14 @@ object MockLibraryUtil { "-d", classesDir.path ) - KotlinTestUtils.compileJavaFiles(javaFiles, options) + val compile = + if (useJava9) KotlinTestUtils::compileJavaFilesExternallyWithJava9 + else KotlinTestUtils::compileJavaFiles + + val success = compile(javaFiles, options) + if (!success) { + throw AssertionError("Java files are not compiled successfully") + } } return createJarFile(contentDir, classesDir, jarName, sourcesPath.takeIf { addSources }) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsWithJdk9Test.java b/compiler/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsWithJdk9Test.java index 03a77c89e69..2cd00e7c60e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsWithJdk9Test.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsWithJdk9Test.java @@ -44,7 +44,7 @@ public abstract class AbstractDiagnosticsWithJdk9Test extends AbstractDiagnostic @NotNull Map modules, @NotNull List testFiles ) { - if (KotlinTestUtils.getJre9HomeIfPossible() == null) { + if (KotlinTestUtils.getJdk9HomeIfPossible() == null) { // Skip this test if no Java 9 is found return; } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt new file mode 100644 index 00000000000..3a1658ed574 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/modules/IdeJavaModuleResolver.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2017 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.idea.modules + +import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiCompiledElement +import com.intellij.psi.PsiJavaModule +import com.intellij.psi.PsiManager +import com.intellij.psi.impl.file.impl.JavaFileManager +import com.intellij.psi.impl.light.LightJavaModule +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.resolve.jvm.modules.* + +class IdeJavaModuleResolver(project: Project) : JavaModuleResolver { + private val psiManager = PsiManager.getInstance(project) + private val fileManager = JavaFileManager.SERVICE.getInstance(project) + private val allScope = GlobalSearchScope.allScope(project) + + override val moduleGraph: JavaModuleGraph = JavaModuleGraph( + object : JavaModuleFinder { + override fun findModule(name: String): JavaModule? { + return fileManager.findModules(name, allScope).singleOrNull()?.toJavaModule() + } + } + ) + + override fun findJavaModule(file: VirtualFile): JavaModule? { + val psiFile = psiManager.findFile(file) ?: return null + return JavaModuleGraphUtil.findDescriptorByElement(psiFile)?.toJavaModule() + } + + private fun PsiJavaModule.toJavaModule(): JavaModule { + if (this is LightJavaModule) { + return JavaModule.Automatic(name, rootVirtualFile) + } + + val virtualFile = containingFile?.virtualFile ?: error("No VirtualFile found for module $this ($javaClass)") + return JavaModule.Explicit(JavaModuleInfo.create(this), virtualFile.parent, virtualFile, this is PsiCompiledElement) + } +} diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/PluginTestCaseBase.java b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/PluginTestCaseBase.java index 0e561b44d2b..f91c1e84f36 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/PluginTestCaseBase.java +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/PluginTestCaseBase.java @@ -19,8 +19,10 @@ package org.jetbrains.kotlin.idea.test; import com.intellij.openapi.projectRoots.JavaSdk; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestJdkKind; import java.io.File; @@ -38,19 +40,36 @@ public class PluginTestCaseBase { @NotNull private static Sdk getSdk(String sdkHome, String name) { - return JavaSdk.getInstance().createJdk(name + " JDK", sdkHome, true); + return JavaSdk.getInstance().createJdk(name, sdkHome, true); } @NotNull public static Sdk mockJdk() { - return getSdk("compiler/testData/mockJDK/jre", "Mock"); + return getSdk("compiler/testData/mockJDK/jre", "Mock JDK"); } @NotNull public static Sdk fullJdk() { String javaHome = System.getProperty("java.home"); assert new File(javaHome).isDirectory(); - return getSdk(javaHome, "Full"); + return getSdk(javaHome, "Full JDK"); + } + + @NotNull + public static Sdk jdk(@NotNull TestJdkKind kind) { + switch (kind) { + case MOCK_JDK: + return mockJdk(); + case FULL_JDK_9: + File jre9 = KotlinTestUtils.getJdk9HomeIfPossible(); + assert jre9 != null : "JDK_19 environment variable is not set"; + VfsRootAccess.allowRootAccess(jre9.getPath()); + return getSdk(jre9.getPath(), "Full JDK 9"); + case FULL_JDK: + return fullJdk(); + default: + throw new UnsupportedOperationException(kind.toString()); + } } public static boolean isAllFilesPresentTest(@NotNull String testName) { diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index de07c6e4151..0e0e36ff850 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -245,6 +245,9 @@ + + diff --git a/idea/testData/multiModuleHighlighting/java9/declarationKinds/dependency/module-info.java b/idea/testData/multiModuleHighlighting/java9/declarationKinds/dependency/module-info.java new file mode 100644 index 00000000000..1bf49a29559 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/declarationKinds/dependency/module-info.java @@ -0,0 +1,2 @@ +module dependency { +} diff --git a/idea/testData/multiModuleHighlighting/java9/declarationKinds/dependency/unexported/declarationKinds.kt b/idea/testData/multiModuleHighlighting/java9/declarationKinds/dependency/unexported/declarationKinds.kt new file mode 100644 index 00000000000..3818da9474e --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/declarationKinds/dependency/unexported/declarationKinds.kt @@ -0,0 +1,12 @@ +package unexported + +class Klass +interface Interface + +typealias TypeAliasToPublic = String +typealias TypeAliasToUnexported = Klass + +fun function() {} + +val valProperty = "" +var varProperty = "" diff --git a/idea/testData/multiModuleHighlighting/java9/declarationKinds/main/module-info.java b/idea/testData/multiModuleHighlighting/java9/declarationKinds/main/module-info.java new file mode 100644 index 00000000000..d069dfb8d7a --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/declarationKinds/main/module-info.java @@ -0,0 +1,3 @@ +module main { + requires dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/declarationKinds/main/usage.kt b/idea/testData/multiModuleHighlighting/java9/declarationKinds/main/usage.kt new file mode 100644 index 00000000000..ad6d5883cb1 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/declarationKinds/main/usage.kt @@ -0,0 +1,17 @@ +import unexported.* + +fun usage(): String { + val k: Klass = Klass() + val i: Interface? = null + + val ta1: TypeAliasToPublic = TypeAliasToPublic() + val ta2: TypeAliasToUnexported = TypeAliasToUnexported() + + function() + + valProperty + varProperty + varProperty = "" + + return "$k$i$ta1$ta2" +} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/dependency/dependency/Foo.java b/idea/testData/multiModuleHighlighting/java9/exportsTo/dependency/dependency/Foo.java new file mode 100644 index 00000000000..cb91cdb9637 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/dependency/dependency/Foo.java @@ -0,0 +1,3 @@ +package dependency; + +public class Foo {} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/dependency/module-info.java b/idea/testData/multiModuleHighlighting/java9/exportsTo/dependency/module-info.java new file mode 100644 index 00000000000..aa45063f48e --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/dependency/module-info.java @@ -0,0 +1,3 @@ +module dependency { + exports dependency to first; +} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/first/firstUsage.kt b/idea/testData/multiModuleHighlighting/java9/exportsTo/first/firstUsage.kt new file mode 100644 index 00000000000..7ee2415b9f2 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/first/firstUsage.kt @@ -0,0 +1,6 @@ +import dependency.Foo + +fun firstUsage(): String { + val foo: Foo = Foo() + return foo.toString() +} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/first/module-info.java b/idea/testData/multiModuleHighlighting/java9/exportsTo/first/module-info.java new file mode 100644 index 00000000000..f217d8c488b --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/first/module-info.java @@ -0,0 +1,3 @@ +module first { + requires dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/second/module-info.java b/idea/testData/multiModuleHighlighting/java9/exportsTo/second/module-info.java new file mode 100644 index 00000000000..3a5db664300 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/second/module-info.java @@ -0,0 +1,3 @@ +module second { + requires dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/second/secondUsage.kt b/idea/testData/multiModuleHighlighting/java9/exportsTo/second/secondUsage.kt new file mode 100644 index 00000000000..d239e64fd38 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/second/secondUsage.kt @@ -0,0 +1,6 @@ +import dependency.Foo + +fun secondUsage(): String { + val foo: Foo = Foo() + return foo.toString() +} diff --git a/idea/testData/multiModuleHighlighting/java9/exportsTo/unnamed/unnamedUsage.kt b/idea/testData/multiModuleHighlighting/java9/exportsTo/unnamed/unnamedUsage.kt new file mode 100644 index 00000000000..a6b343c3ada --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/exportsTo/unnamed/unnamedUsage.kt @@ -0,0 +1,6 @@ +import dependency.Foo + +fun unnamedUsage(): String { + val foo: Foo = Foo() + return foo.toString() +} diff --git a/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/dependency/dependency/J.java b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/dependency/dependency/J.java new file mode 100644 index 00000000000..dfd02f230b1 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/dependency/dependency/J.java @@ -0,0 +1,4 @@ +package dependency; + +public class J { +} diff --git a/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/module-info.java b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/module-info.java new file mode 100644 index 00000000000..d069dfb8d7a --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/module-info.java @@ -0,0 +1,3 @@ +module main { + requires dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt new file mode 100644 index 00000000000..a03370a901c --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/namedDependsOnUnnamed/main/usage.kt @@ -0,0 +1,6 @@ +import dependency.J + +fun usage(): String { + val j: J = J() + return j.toString() +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/J.java b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/J.java new file mode 100644 index 00000000000..fbbbdfa6037 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/J.java @@ -0,0 +1,7 @@ +package dependency; + +import dependency.impl.JImpl; + +public class J { + public static JImpl getInstance() { return new JImpl(); } +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/K.kt b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/K.kt new file mode 100644 index 00000000000..c9e701dd7c5 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/K.kt @@ -0,0 +1,9 @@ +package dependency + +import dependency.impl.KImpl + +open class K { + companion object { + fun getInstance(): KImpl = KImpl() + } +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/impl/JImpl.java b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/impl/JImpl.java new file mode 100644 index 00000000000..24efaacbfe0 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/impl/JImpl.java @@ -0,0 +1,6 @@ +package dependency.impl; + +import dependency.J; + +public class JImpl extends J { +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/impl/KImpl.kt b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/impl/KImpl.kt new file mode 100644 index 00000000000..389e1b0cc6f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/dependency/impl/KImpl.kt @@ -0,0 +1,5 @@ +package dependency.impl + +import dependency.K + +class KImpl : K() diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/module-info.java b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/module-info.java new file mode 100644 index 00000000000..4f71cf3e223 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/library/module-info.java @@ -0,0 +1,3 @@ +module library { + exports dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/main/module-info.java b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/main/module-info.java new file mode 100644 index 00000000000..e236e735339 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/main/module-info.java @@ -0,0 +1,3 @@ +module main { + requires library; +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/main/usage.kt b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/main/usage.kt new file mode 100644 index 00000000000..d90a85f266f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleLibraryExportsPackage/main/usage.kt @@ -0,0 +1,16 @@ +import dependency.* +import dependency.J +import dependency.K +import dependency.impl.* +import dependency.impl.JImpl +import dependency.impl.KImpl + +fun usage(): String { + val j: J = J.getInstance() + val k: K = K.getInstance() + + val jImpl: JImpl = J.getInstance() + val kImpl: KImpl = K.getInstance() + + return "$j$k$jImpl$kImpl" +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/J.java b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/J.java new file mode 100644 index 00000000000..fbbbdfa6037 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/J.java @@ -0,0 +1,7 @@ +package dependency; + +import dependency.impl.JImpl; + +public class J { + public static JImpl getInstance() { return new JImpl(); } +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/K.kt b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/K.kt new file mode 100644 index 00000000000..c9e701dd7c5 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/K.kt @@ -0,0 +1,9 @@ +package dependency + +import dependency.impl.KImpl + +open class K { + companion object { + fun getInstance(): KImpl = KImpl() + } +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/impl/JImpl.java b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/impl/JImpl.java new file mode 100644 index 00000000000..24efaacbfe0 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/impl/JImpl.java @@ -0,0 +1,6 @@ +package dependency.impl; + +import dependency.J; + +public class JImpl extends J { +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/impl/KImpl.kt b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/impl/KImpl.kt new file mode 100644 index 00000000000..389e1b0cc6f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/dependency/impl/KImpl.kt @@ -0,0 +1,5 @@ +package dependency.impl + +import dependency.K + +class KImpl : K() diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/module-info.java b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/module-info.java new file mode 100644 index 00000000000..577768d30c4 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/dependency/module-info.java @@ -0,0 +1,3 @@ +module dependency { + exports dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/main/module-info.java b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/main/module-info.java new file mode 100644 index 00000000000..d069dfb8d7a --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/main/module-info.java @@ -0,0 +1,3 @@ +module main { + requires dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/main/usage.kt b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/main/usage.kt new file mode 100644 index 00000000000..d90a85f266f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/simpleModuleExportsPackage/main/usage.kt @@ -0,0 +1,16 @@ +import dependency.* +import dependency.J +import dependency.K +import dependency.impl.* +import dependency.impl.JImpl +import dependency.impl.KImpl + +fun usage(): String { + val j: J = J.getInstance() + val k: K = K.getInstance() + + val jImpl: JImpl = J.getInstance() + val kImpl: KImpl = K.getInstance() + + return "$j$k$jImpl$kImpl" +} diff --git a/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/J.java b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/J.java new file mode 100644 index 00000000000..fbbbdfa6037 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/J.java @@ -0,0 +1,7 @@ +package dependency; + +import dependency.impl.JImpl; + +public class J { + public static JImpl getInstance() { return new JImpl(); } +} diff --git a/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/K.kt b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/K.kt new file mode 100644 index 00000000000..c9e701dd7c5 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/K.kt @@ -0,0 +1,9 @@ +package dependency + +import dependency.impl.KImpl + +open class K { + companion object { + fun getInstance(): KImpl = KImpl() + } +} diff --git a/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/impl/JImpl.java b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/impl/JImpl.java new file mode 100644 index 00000000000..24efaacbfe0 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/impl/JImpl.java @@ -0,0 +1,6 @@ +package dependency.impl; + +import dependency.J; + +public class JImpl extends J { +} diff --git a/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/impl/KImpl.kt b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/impl/KImpl.kt new file mode 100644 index 00000000000..389e1b0cc6f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/dependency/impl/KImpl.kt @@ -0,0 +1,5 @@ +package dependency.impl + +import dependency.K + +class KImpl : K() diff --git a/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/module-info.java b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/module-info.java new file mode 100644 index 00000000000..577768d30c4 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/dependency/module-info.java @@ -0,0 +1,3 @@ +module dependency { + exports dependency; +} diff --git a/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/main/usage.kt b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/main/usage.kt new file mode 100644 index 00000000000..d90a85f266f --- /dev/null +++ b/idea/testData/multiModuleHighlighting/java9/unnamedDependsOnNamed/main/usage.kt @@ -0,0 +1,16 @@ +import dependency.* +import dependency.J +import dependency.K +import dependency.impl.* +import dependency.impl.JImpl +import dependency.impl.KImpl + +fun usage(): String { + val j: J = J.getInstance() + val k: K = K.getInstance() + + val jImpl: JImpl = J.getInstance() + val kImpl: KImpl = K.getInstance() + + return "$j$k$jImpl$kImpl" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt index f75e2a6e3a4..1cdd28e1fa4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt @@ -20,10 +20,11 @@ import com.intellij.openapi.module.Module import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.config.TargetPlatformKind import org.jetbrains.kotlin.idea.stubs.AbstractMultiHighlightingTest +import org.jetbrains.kotlin.test.TestJdkKind abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTest() { - protected fun checkHighlightingInAllFiles( + protected open fun checkHighlightingInAllFiles( shouldCheckFile: () -> Boolean = { !file.text.contains("// !CHECK_HIGHLIGHTING") } ) { checkFiles(shouldCheckFile) { @@ -35,9 +36,9 @@ abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTe vararg platforms: TargetPlatformKind<*>, withStdlibCommon: Boolean = false, configureModule: (Module, TargetPlatformKind<*>) -> Unit = { _, _ -> }, - useFullJdk: Boolean = false + jdk: TestJdkKind = TestJdkKind.MOCK_JDK ) { - val commonModule = module("common", useFullJdk = useFullJdk) + val commonModule = module("common", jdk) commonModule.createFacet(TargetPlatformKind.Common) if (withStdlibCommon) { commonModule.addLibrary(ForTestCompileRuntime.stdlibCommonForTests()) @@ -49,7 +50,7 @@ abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTe is TargetPlatformKind.JavaScript -> "js" else -> error("Unsupported platform: $platform") } - val platformModule = module(path, useFullJdk = useFullJdk) + val platformModule = module(path, jdk) platformModule.createFacet(platform) platformModule.enableMultiPlatform() platformModule.addDependency(commonModule) diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java8MultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java8MultiModuleHighlightingTest.kt index 11b77d36ea6..b0cc34728f7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java8MultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java8MultiModuleHighlightingTest.kt @@ -17,12 +17,13 @@ package org.jetbrains.kotlin.idea.caches.resolve import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.test.TestJdkKind class Java8MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleHighlighting/" fun testDifferentJdk() { - val module1 = module("jdk8", useFullJdk = true) + val module1 = module("jdk8", TestJdkKind.FULL_JDK) val module2 = module("mockJdk") module1.addDependency(module2) diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java9MultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java9MultiModuleHighlightingTest.kt new file mode 100644 index 00000000000..5df92a15657 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/Java9MultiModuleHighlightingTest.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2017 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.idea.caches.resolve + +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.MockLibraryUtil +import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK_9 + +class Java9MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { + override fun getTestDataPath(): String = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleHighlighting/java9/" + + private inline fun doTest(test: () -> Unit) { + // Skip this test if no Java 9 is found + if (KotlinTestUtils.getJdk9HomeIfPossible() != null) { + test() + } + } + + private fun module(name: String): Module = super.module(name, FULL_JDK_9, false) + + fun testSimpleModuleExportsPackage() = doTest { + module("main").addDependency(module("dependency")) + checkHighlightingInAllFiles() + } + + fun testSimpleLibraryExportsPackage() = doTest { + val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: return + val library = MockLibraryUtil.compileJvmLibraryToJar( + testDataPath + "${getTestName(true)}/library", "library", + extraOptions = listOf("-jdk-home", jdk9Home.path), + useJava9 = true + ) + + module("main").addLibrary(library, "library") + checkHighlightingInAllFiles() + } + + fun testNamedDependsOnUnnamed() = doTest { + module("main").addDependency(module("dependency")) + checkHighlightingInAllFiles() + } + + fun testUnnamedDependsOnNamed() = doTest { + module("main").addDependency(module("dependency")) + checkHighlightingInAllFiles() + } + + fun testDeclarationKinds() = doTest { + module("main").addDependency(module("dependency")) + checkHighlightingInAllFiles() + } + + fun testExportsTo() = doTest { + val d = module("dependency") + module("first").addDependency(d) + module("second").addDependency(d) + module("unnamed").addDependency(d) + checkHighlightingInAllFiles() + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt index 093e9141d45..927098d8532 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration import org.jetbrains.kotlin.idea.facet.KotlinFacetType import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleHighlighting/" @@ -110,10 +111,10 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { } fun testLanguageVersionsViaFacets() { - val m1 = module("m1", useFullJdk = true).setupKotlinFacet { + val m1 = module("m1", FULL_JDK).setupKotlinFacet { settings.languageLevel = LanguageVersion.KOTLIN_1_1 } - val m2 = module("m2", useFullJdk = true).setupKotlinFacet { + val m2 = module("m2", FULL_JDK).setupKotlinFacet { settings.languageLevel = LanguageVersion.KOTLIN_1_0 } @@ -174,7 +175,7 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { fun testUseCorrectBuiltInsForCommonModule() { doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], TargetPlatformKind.JavaScript, - withStdlibCommon = true, useFullJdk = true, configureModule = { module, platform -> + withStdlibCommon = true, jdk = FULL_JDK, configureModule = { module, platform -> if (platform == TargetPlatformKind.JavaScript) { module.addLibrary(ForTestCompileRuntime.stdlibJsForTests()) module.addLibrary(ForTestCompileRuntime.stdlibCommonForTests()) diff --git a/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt b/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt index 647cb9d54c0..e4e62d596f1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.idea.test.KotlinJdkAndLibraryProjectDescriptor import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestJdkKind import org.junit.Assert import java.io.File @@ -53,15 +54,14 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() { VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory()) } - protected fun module(name: String, hasTestRoot: Boolean = false, useFullJdk: Boolean = false): Module { + protected fun module(name: String, jdk: TestJdkKind = TestJdkKind.MOCK_JDK, hasTestRoot: Boolean = false): Module { val srcDir = testDataPath + "${getTestName(true)}/$name" val moduleWithSrcRootSet = createModuleFromTestData(srcDir, name, StdModuleTypes.JAVA, true)!! if (hasTestRoot) { setTestRoot(moduleWithSrcRootSet, name) } - val jdkToUse = if (useFullJdk) PluginTestCaseBase.fullJdk() else PluginTestCaseBase.mockJdk() - ConfigLibraryUtil.configureSdk(moduleWithSrcRootSet, jdkToUse) + ConfigLibraryUtil.configureSdk(moduleWithSrcRootSet, PluginTestCaseBase.jdk(jdk)) return moduleWithSrcRootSet } @@ -86,9 +86,9 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() { exported: Boolean = false ) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported) - protected fun Module.addLibrary(jar: File) { + protected fun Module.addLibrary(jar: File, name: String = KotlinJdkAndLibraryProjectDescriptor.LIBRARY_NAME) { ConfigLibraryUtil.addLibrary(NewLibraryEditor().apply { - name = KotlinJdkAndLibraryProjectDescriptor.LIBRARY_NAME + this.name = name addRoot(VfsUtil.getUrlForLibraryRoot(jar), OrderRootType.CLASSES) }, this) } diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt index 2706d875e56..7a484fa415f 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt @@ -939,7 +939,7 @@ class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { } fun testJre9() { - val path = KotlinTestUtils.getJre9HomeIfPossible()?.absolutePath ?: return + val path = KotlinTestUtils.getJdk9HomeIfPossible()?.absolutePath ?: return val jdk = myModel.global.addSdk(JDK_NAME, path, "9", JpsJavaSdkType.INSTANCE) jdk.addRoot(StandardFileSystems.JRT_PROTOCOL_PREFIX + path + URLUtil.JAR_SEPARATOR + "java.base", JpsOrderRootType.COMPILED)