From 45190d9453003d19f98bc612cdb96d09df04b4c1 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 27 Sep 2016 18:50:33 +0300 Subject: [PATCH] Support separate modules in compiler Unless the compatibility option "-Xsingle-module" is passed, the compiler will create two modules instead of one now (see TopDownAnalyzerFacadeForJVM): the main module contains Kotlin and Java sources and binaries from the previous compilation of the given module chunk, the dependency module contains all other Kotlin and Java binaries. This fixes some issues where the compiler couldn't detect that the used symbol was from another module, and did not forbid some usages which are only possible inside the module (see KT-10001). The ideal way to deal with modules here would be to exactly recreate the project structure, for example as it's done in JvmAnalyzerFacade and usages. This is postponed until later #KT-10001 Fixed #KT-11840 In Progress --- .../arguments/K2JVMCompilerArguments.java | 3 + .../jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt | 1 + .../jvm/compiler/JvmCliVirtualFileFinder.kt | 10 +- .../JvmCliVirtualFileFinderFactory.kt | 3 +- .../compiler/KotlinCliJavaFileManagerImpl.kt | 12 +- .../cli/jvm/compiler/KotlinCoreEnvironment.kt | 5 +- .../compiler/KotlinToJVMBytecodeCompiler.kt | 41 ++++-- .../kotlin/config/JVMConfigurationKeys.java | 3 + .../kotlin/frontend/java/di/injection.kt | 5 +- .../jvm/TopDownAnalyzerFacadeForJVM.kt | 119 ++++++++++++++---- compiler/testData/cli/jvm/extraHelp.out | 1 + .../testsWithStdLib/regression/kt10001.kt | 9 ++ .../testsWithStdLib/regression/kt10001.txt | 4 + .../DiagnosticsTestWithStdLibGenerated.java | 6 + .../intentions/IntentionTestGenerated.java | 12 +- 15 files changed, 182 insertions(+), 52 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.txt diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.java b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.java index 89cdef56761..7370348cf9e 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.java +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.java @@ -95,6 +95,9 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments { @Argument(value = "Xload-script-configs", description = "Load script configuration files from project directory tree") public boolean loadScriptConfigs; + @Argument(value = "Xsingle-module", description = "Combine modules for source files and binary dependencies into a single module") + public boolean singleModule; + // Paths to output directories for friend modules. public String[] friendPaths; diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt index b2fca041d77..a69ed5a69f3 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt @@ -366,6 +366,7 @@ class K2JVMCompiler : CLICompiler() { configuration.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage) configuration.put(CLIConfigurationKeys.REPORT_PERF, arguments.reportPerf) configuration.put(JVMConfigurationKeys.LOAD_SCRIPT_CONFIGS, arguments.loadScriptConfigs) + configuration.put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule) arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinder.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinder.kt index c011cf16f4b..261b7682fad 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinder.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinder.kt @@ -17,17 +17,21 @@ package org.jetbrains.kotlin.cli.jvm.compiler import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClassFinder import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.utils.addToStdlib.check -class JvmCliVirtualFileFinder(private val index: JvmDependenciesIndex) : VirtualFileKotlinClassFinder() { - +class JvmCliVirtualFileFinder( + private val index: JvmDependenciesIndex, + private val scope: GlobalSearchScope +) : VirtualFileKotlinClassFinder() { override fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? { val classFileName = classId.relativeClassName.asString().replace('.', '$') return index.findClass(classId, acceptedRootTypes = JavaRoot.OnlyBinary) { dir, rootType -> dir.findChild("$classFileName.class")?.let { if (it.isValid) it else null } - } + }?.check { it in scope } } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinderFactory.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinderFactory.kt index 0ae0175dbe8..cdc97fa5487 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinderFactory.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JvmCliVirtualFileFinderFactory.kt @@ -20,6 +20,7 @@ import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinderFactory +// TODO: create different JvmDependenciesIndex instances for different sets of source roots to improve performance class JvmCliVirtualFileFinderFactory(private val index: JvmDependenciesIndex) : JvmVirtualFileFinderFactory { - override fun create(scope: GlobalSearchScope): JvmVirtualFileFinder = JvmCliVirtualFileFinder(index) + override fun create(scope: GlobalSearchScope): JvmVirtualFileFinder = JvmCliVirtualFileFinder(index, scope) } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt index 11e6c640d42..4e55acf39f0 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt @@ -30,14 +30,14 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager import org.jetbrains.kotlin.util.PerformanceCounter -import java.util.ArrayList +import org.jetbrains.kotlin.utils.addToStdlib.check +import java.util.* import kotlin.properties.Delegates -class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) -: CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager { - +class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager { private val perfCounter = PerformanceCounter.create("Find Java class") private var index: JvmDependenciesIndex by Delegates.notNull() + private val allScope = GlobalSearchScope.allScope(myPsiManager.project) fun initIndex(packagesCache: JvmDependenciesIndex) { this.index = packagesCache @@ -47,8 +47,8 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) return perfCounter.time { val classNameWithInnerClasses = classId.relativeClassName.asString() index.findClass(classId) { dir, type -> - findClassGivenPackage(searchScope, dir, classNameWithInnerClasses, type) - } + findClassGivenPackage(allScope, dir, classNameWithInnerClasses, type) + }?.check { it.containingFile.virtualFile in searchScope } } } 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 05ff2c15d28..91f907e45ca 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 @@ -260,7 +260,7 @@ class KotlinCoreEnvironment private constructor( private fun findLocalDirectory(root: JvmContentRoot): VirtualFile? { val path = root.file - val localFile = applicationEnvironment.localFileSystem.findFileByPath(path.absolutePath) + val localFile = findLocalDirectory(path.absolutePath) if (localFile == null) { report(WARNING, "Classpath entry points to a non-existent location: $path") return null @@ -268,6 +268,9 @@ class KotlinCoreEnvironment private constructor( return localFile } + internal fun findLocalDirectory(absolutePath: String): VirtualFile? = + applicationEnvironment.localFileSystem.findFileByPath(absolutePath) + private fun findJarRoot(root: JvmClasspathRoot): VirtualFile? { val path = root.file val jarFile = applicationEnvironment.jarFileSystem.findFileByPath("${path}${URLUtil.JAR_SEPARATOR}") diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt index f4131a3dbbc..d382dbb19be 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt @@ -16,9 +16,14 @@ package org.jetbrains.kotlin.cli.jvm.compiler +import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.JarUtil +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiManager import com.intellij.psi.impl.PsiModificationTrackerImpl +import com.intellij.psi.search.DelegatingGlobalSearchScope +import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics @@ -402,14 +407,24 @@ object KotlinToJVMBytecodeCompiler { val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector) analyzerWithCompilerReport.analyzeAndReport( environment.getSourceFiles(), object : AnalyzerWithCompilerReport.Analyzer { - override fun analyze(): AnalysisResult = - TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( - environment.project, - environment.getSourceFiles(), - CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(), - environment.configuration, - { scope -> JvmPackagePartProvider(environment, scope) } - ) + override fun analyze(): AnalysisResult { + val project = environment.project + val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNull { module -> + environment.findLocalDirectory(module.getOutputDirectory()) + }.orEmpty() + val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, environment.getSourceFiles()) + // To support partial and incremental compilation, we add the scope which contains binaries from output directories + // of the compiled modules (.class) to the list of scopes of the source module + val scope = if (moduleOutputs.isEmpty()) sourcesOnly else sourcesOnly.uniteWith(DirectoriesScope(project, moduleOutputs)) + return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( + project, + environment.getSourceFiles(), + CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace(), + environment.configuration, + { scope -> JvmPackagePartProvider(environment, scope) }, + scope + ) + } override fun reportEnvironmentErrors() { reportRuntimeConflicts(collector, environment.configuration.jvmClasspathRoots) @@ -436,6 +451,16 @@ object KotlinToJVMBytecodeCompiler { null } + class DirectoriesScope( + project: Project, private val directories: List + ) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) { + // TODO: optimize somehow? + override fun contains(file: VirtualFile) = + directories.any { directory -> VfsUtilCore.isAncestor(directory, file, false) } + + override fun toString() = "All files under: $directories" + } + private fun generate( environment: KotlinCoreEnvironment, configuration: CompilerConfiguration, diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index 8f98f9827f2..f36a99f10f3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -56,6 +56,9 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey USE_TYPE_TABLE = CompilerConfigurationKey.create("use type table in serializer"); + public static final CompilerConfigurationKey USE_SINGLE_MODULE = + CompilerConfigurationKey.create("combine modules for source files and binary dependencies into a single module"); + public static final CompilerConfigurationKey JVM_TARGET = CompilerConfigurationKey.create("JVM bytecode target version"); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt index 9c685fcb69c..a5aa9379e93 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt @@ -118,9 +118,7 @@ fun createContainerForTopDownAnalyzerForJvm( ): ComponentProvider = createContainerForLazyResolveWithJava( moduleContext, bindingTrace, declarationProviderFactory, moduleContentScope, moduleClassResolver, CompilerEnvironment, lookupTracker, packagePartProvider, languageVersionSettings, useLazyResolve = false -).apply { - initJvmBuiltInsForTopDownAnalysis(moduleContext.module, languageVersionSettings) -} +) fun createContainerForTopDownSingleModuleAnalyzerForJvm( @@ -134,6 +132,7 @@ fun createContainerForTopDownSingleModuleAnalyzerForJvm( moduleContext, bindingTrace, declarationProviderFactory, moduleContentScope, LookupTracker.DO_NOTHING, packagePartProvider, languageVersionSettings, SingleModuleClassResolver() ).apply { + initJvmBuiltInsForTopDownAnalysis(moduleContext.module, languageVersionSettings) get().resolver = get() } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.kt index a25deed9eef..90543bf9588 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/TopDownAnalyzerFacadeForJVM.kt @@ -16,7 +16,10 @@ package org.jetbrains.kotlin.resolve.jvm +import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.search.DelegatingGlobalSearchScope import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.config.CommonConfigurationKeys @@ -27,13 +30,17 @@ import org.jetbrains.kotlin.container.get import org.jetbrains.kotlin.context.ContextForNewModule import org.jetbrains.kotlin.context.MutableModuleContext import org.jetbrains.kotlin.context.ProjectContext +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.PackagePartProvider import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDependenciesImpl import org.jetbrains.kotlin.frontend.java.di.createContainerForTopDownAnalyzerForJvm +import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis import org.jetbrains.kotlin.incremental.components.LookupTracker -import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl +import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver import org.jetbrains.kotlin.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.load.kotlin.DeserializationComponentsForJava import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackagePartProvider @@ -48,19 +55,23 @@ import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExten import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import java.util.* object TopDownAnalyzerFacadeForJVM { @JvmStatic + @JvmOverloads fun analyzeFilesWithJavaIntegration( project: Project, files: Collection, trace: BindingTrace, configuration: CompilerConfiguration, - packagePartProviderFactory: (GlobalSearchScope) -> PackagePartProvider + packagePartProviderFactory: (GlobalSearchScope) -> PackagePartProvider, + sourceModuleSearchScope: GlobalSearchScope = newModuleSearchScope(project, files) ): AnalysisResult { - val moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(project, configuration) + val moduleContext = createModuleContext(project, configuration) + val storageManager = moduleContext.storageManager val module = moduleContext.module @@ -68,29 +79,51 @@ object TopDownAnalyzerFacadeForJVM { val lookupTracker = incrementalComponents?.getLookupTracker() ?: LookupTracker.DO_NOTHING val targetIds = configuration.get(JVMConfigurationKeys.MODULES)?.map(::TargetId) - val resolverByClass = object : (JavaClass) -> JavaDescriptorResolver { - lateinit var resolver: JavaDescriptorResolver + val separateModules = !configuration.getBoolean(JVMConfigurationKeys.USE_SINGLE_MODULE) - override fun invoke(javaClass: JavaClass): JavaDescriptorResolver { - return resolver - } + val sourceScope = if (separateModules) sourceModuleSearchScope else GlobalSearchScope.allScope(project) + val moduleClassResolver = SourceOrBinaryModuleClassResolver(sourceScope) + + val languageVersionSettings = + configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT) + + val dependencyModule = if (separateModules) { + val dependenciesContext = ContextForNewModule( + moduleContext, Name.special(""), + JvmPlatform, module.builtIns + ) + + // Scope for the dependency module contains everything except files present in the scope for the source module + val dependencyScope = GlobalSearchScope.notScope(sourceScope) + + val dependenciesContainer = createContainerForTopDownAnalyzerForJvm( + dependenciesContext, trace, DeclarationProviderFactory.EMPTY, dependencyScope, lookupTracker, + packagePartProviderFactory(dependencyScope), languageVersionSettings, moduleClassResolver + ) + + moduleClassResolver.compiledCodeResolver = dependenciesContainer.get() + + dependenciesContext.setDependencies(dependenciesContext.module, dependenciesContext.module.builtIns.builtInsModule) + dependenciesContext.initializeModuleContents(moduleClassResolver.compiledCodeResolver.packageFragmentProvider) + dependenciesContext.module } + else null - val sourceScope = GlobalSearchScope.allScope(project) + // Note that it's necessary to create container for sources _after_ creation of container for dependencies because + // CliLightClassGenerationSupport#initialize is invoked when container is created, so only the last module descriptor is going + // to be stored in CliLightClassGenerationSupport, and it better be the source one (otherwise light classes would not be found) + // TODO: get rid of duplicate invocation of CodeAnalyzerInitializer#initialize, or refactor CliLightClassGenerationSupport val container = createContainerForTopDownAnalyzerForJvm( - moduleContext, - trace, - FileBasedDeclarationProviderFactory(storageManager, files), - sourceScope, - lookupTracker, + moduleContext, trace, FileBasedDeclarationProviderFactory(storageManager, files), sourceScope, lookupTracker, IncrementalPackagePartProvider.create( packagePartProviderFactory(sourceScope), targetIds, incrementalComponents, storageManager ), - configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT), - ModuleClassResolverImpl(resolverByClass) - ) - resolverByClass.resolver = container.get() + languageVersionSettings, moduleClassResolver + ).apply { + initJvmBuiltInsForTopDownAnalysis(module, languageVersionSettings) + } + moduleClassResolver.sourceCodeResolver = container.get() val additionalProviders = ArrayList() if (incrementalComponents != null) { @@ -104,10 +137,16 @@ object TopDownAnalyzerFacadeForJVM { additionalProviders.add(container.get().packageFragmentProvider) + // TODO: consider putting extension package fragment providers into the dependency module PackageFragmentProviderExtension.getInstances(project).mapNotNullTo(additionalProviders) { extension -> extension.getPackageFragmentProvider(project, module, storageManager, trace, null) } + // TODO: remove dependencyModule from friends + module.setDependencies(ModuleDependenciesImpl( + listOfNotNull(module, dependencyModule, module.builtIns.builtInsModule), + if (dependencyModule != null) setOf(dependencyModule) else emptySet() + )) module.initialize(CompositePackageFragmentProvider( listOf(container.get().packageFragmentProvider) + additionalProviders @@ -123,14 +162,46 @@ object TopDownAnalyzerFacadeForJVM { return AnalysisResult.success(trace.bindingContext, module) } - fun createContextWithSealedModule(project: Project, configuration: CompilerConfiguration): MutableModuleContext { + fun newModuleSearchScope(project: Project, files: Collection): GlobalSearchScope { + // In case of separate modules, the source module scope generally consists of the following scopes: + // 1) scope which only contains passed Kotlin source files (.kt and .kts) + // 2) scope which contains all Java source files (.java) in the project + return GlobalSearchScope.filesScope(project, files.map { it.virtualFile }.toSet()).uniteWith(AllJavaSourcesInProjectScope(project)) + } + + // TODO: limit this scope to the Java source roots, which the module has in its CONTENT_ROOTS + class AllJavaSourcesInProjectScope(project: Project) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) { + // 'isDirectory' check is needed because otherwise directories such as 'frontend.java' would be recognized + // as Java source files, which makes no sense + override fun contains(file: VirtualFile) = + file.fileType === JavaFileType.INSTANCE && !file.isDirectory + + override fun toString() = "All Java sources in the project" + } + + class SourceOrBinaryModuleClassResolver(private val sourceScope: GlobalSearchScope) : ModuleClassResolver { + lateinit var compiledCodeResolver: JavaDescriptorResolver + lateinit var sourceCodeResolver: JavaDescriptorResolver + + override fun resolveClass(javaClass: JavaClass): ClassDescriptor? { + val resolver = if (javaClass is JavaClassImpl && javaClass.psi.containingFile.virtualFile in sourceScope) + sourceCodeResolver + else + compiledCodeResolver + return resolver.resolveClass(javaClass) + } + } + + fun createContextWithSealedModule(project: Project, configuration: CompilerConfiguration): MutableModuleContext = + createModuleContext(project, configuration).apply { + setDependencies(module, module.builtIns.builtInsModule) + } + + private fun createModuleContext(project: Project, configuration: CompilerConfiguration): MutableModuleContext { val projectContext = ProjectContext(project) - val builtIns = JvmBuiltIns(projectContext.storageManager) - val context = ContextForNewModule( + return ContextForNewModule( projectContext, Name.special("<${configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)}>"), - JvmPlatform, builtIns + JvmPlatform, JvmBuiltIns(projectContext.storageManager) ) - context.setDependencies(context.module, builtIns.builtInsModule) - return context } } diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index 18fed94cde4..b1464cf1042 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -9,6 +9,7 @@ where advanced options include: -Xskip-metadata-version-check Try loading binary incompatible classes, may cause crashes -Xdump-declarations-to Path to JSON file to dump Java to Kotlin declaration mappings -Xload-script-configs Load script configuration files from project directory tree + -Xsingle-module Combine modules for source files and binary dependencies into a single module -Xno-inline Disable method inlining -Xrepeat Repeat compilation (for performance analysis) -Xplugin Load plugins from the given classpath diff --git a/compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.kt b/compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.kt new file mode 100644 index 00000000000..9c891674d7e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.kt @@ -0,0 +1,9 @@ +fun foo1(p: Pair): Int { + if (p.first != null) return p.first!! + return p.second +} + +fun foo2(p: Pair): Int { + if (p.first != null) return p.first + return p.second +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.txt b/compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.txt new file mode 100644 index 00000000000..098b7a26a02 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.txt @@ -0,0 +1,4 @@ +package + +public fun foo1(/*0*/ p: kotlin.Pair): kotlin.Int +public fun foo2(/*0*/ p: kotlin.Pair): kotlin.Int diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index a83d2ad474e..ee81f30d747 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1054,6 +1054,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW doTest(fileName); } + @TestMetadata("kt10001.kt") + public void testKt10001() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/regression/kt10001.kt"); + doTest(fileName); + } + @TestMetadata("kt2082.kt") public void testKt2082() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/regression/kt2082.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index f0eb97e981e..51550e9fea2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4774,18 +4774,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } - @TestMetadata("withComposedModifiers.kt") - public void testWithComposedModifiers() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt"); - doTest(fileName); - } - @TestMetadata("withComments.kt") public void testWithComments() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withComments.kt"); doTest(fileName); } + @TestMetadata("withComposedModifiers.kt") + public void testWithComposedModifiers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt"); + doTest(fileName); + } + @TestMetadata("withDelegation.kt") public void testWithDelegation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withDelegation.kt");