diff --git a/TeamCityBuild.xml.as32 b/TeamCityBuild.xml.as32 new file mode 100644 index 00000000000..52adddc64bb --- /dev/null +++ b/TeamCityBuild.xml.as32 @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @{prop}=${@{prop}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/buildSrc/gradle.properties.as32 b/buildSrc/gradle.properties.as32 new file mode 100644 index 00000000000..5f8abd5de3c --- /dev/null +++ b/buildSrc/gradle.properties.as32 @@ -0,0 +1,9 @@ +org.gradle.daemon=true +org.gradle.parallel=false +org.gradle.configureondemand=false +org.gradle.jvmargs=-Duser.country=US -Dkotlin.daemon.jvm.options=-Xmx1600m + +#buildSrc.kotlin.repo=https://jcenter.bintray.com +#buildSrc.kotlin.version=1.1.50 + +intellijUltimateEnabled=false diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidJpsBuildTestCase.java.as32 b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidJpsBuildTestCase.java.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidRunner.java.as32 b/compiler/android-tests/tests/org/jetbrains/kotlin/android/tests/AndroidRunner.java.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt.as32 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt.as32 new file mode 100644 index 00000000000..5478ad70e2a --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/ClasspathRootsResolver.kt.as32 @@ -0,0 +1,335 @@ +/* + * 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.compiler + +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiJavaModule +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.PsiManager +import com.intellij.psi.impl.light.LightJavaModule +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageUtil +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot +import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot +import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot +import org.jetbrains.kotlin.cli.jvm.index.JavaRoot +import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder +import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph +import org.jetbrains.kotlin.config.ContentRoot +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.isValidJavaFqName +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo +import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME +import java.io.IOException +import java.util.jar.Attributes +import java.util.jar.Manifest +import kotlin.LazyThreadSafetyMode.NONE + +class ClasspathRootsResolver( + private val psiManager: PsiManager, + private val messageCollector: MessageCollector?, + private val additionalModules: List, + private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?, + private val javaModuleFinder: CliJavaModuleFinder, + private val requireStdlibModule: Boolean, + private val outputDirectory: VirtualFile? +) { + val javaModuleGraph = JavaModuleGraph(javaModuleFinder) + + data class RootsAndModules(val roots: List, val modules: List) + + private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?) + + fun convertClasspathRoots(contentRoots: List): RootsAndModules { + val javaSourceRoots = mutableListOf() + val jvmClasspathRoots = mutableListOf() + val jvmModulePathRoots = mutableListOf() + + for (contentRoot in contentRoots) { + if (contentRoot !is JvmContentRoot) continue + val root = contentRootToVirtualFile(contentRoot) ?: continue + when (contentRoot) { + is JavaSourceRoot -> javaSourceRoots += RootWithPrefix(root, contentRoot.packagePrefix) + is JvmClasspathRoot -> jvmClasspathRoots += root + is JvmModulePathRoot -> jvmModulePathRoots += root + else -> error("Unknown root type: $contentRoot") + } + } + + return computeRoots(javaSourceRoots, jvmClasspathRoots, jvmModulePathRoots) + } + + private fun computeRoots( + javaSourceRoots: List, + jvmClasspathRoots: List, + jvmModulePathRoots: List + ): RootsAndModules { + val result = mutableListOf() + val modules = mutableListOf() + + val hasOutputDirectoryInClasspath = outputDirectory in jvmClasspathRoots || outputDirectory in jvmModulePathRoots + + for ((root, packagePrefix) in javaSourceRoots) { + val modularRoot = modularSourceRoot(root, hasOutputDirectoryInClasspath) + if (modularRoot != null) { + modules += modularRoot + } + else { + result += JavaRoot(root, JavaRoot.RootType.SOURCE, packagePrefix?.let { prefix -> + if (isValidJavaFqName(prefix)) FqName(prefix) + else null.also { + report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix") + } + }) + } + } + + for (root in jvmClasspathRoots) { + result += JavaRoot(root, JavaRoot.RootType.BINARY) + } + + val outputDirectoryAddedAsPartOfModule = modules.any { module -> module.moduleRoots.any { it.file == outputDirectory } } + + for (root in jvmModulePathRoots) { + // Do not add output directory as a separate module if we're compiling an explicit named module. + // It's going to be included as a root of our module in modularSourceRoot. + if (outputDirectoryAddedAsPartOfModule && root == outputDirectory) continue + + val module = modularBinaryRoot(root) + if (module != null) { + modules += module + } + } + + addModularRoots(modules, result) + + return RootsAndModules(result, modules) + } + +/* + private fun findSourceModuleInfo(root: VirtualFile): Pair? { + val moduleInfoFile = + when { + root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE) + root.name == PsiJavaModule.MODULE_INFO_FILE -> root + else -> null + } ?: return null + + val psiFile = psiManager.findFile(moduleInfoFile) ?: return null + val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null + + return moduleInfoFile to psiJavaModule + } + */ + + private fun modularSourceRoot(root: VirtualFile, hasOutputDirectoryInClasspath: Boolean): JavaModule.Explicit? { + /* + val (moduleInfoFile, psiJavaModule) = findSourceModuleInfo(root) ?: return null + val sourceRoot = JavaModule.Root(root, isBinary = false) + val roots = + if (hasOutputDirectoryInClasspath) + listOf(sourceRoot, JavaModule.Root(outputDirectory!!, isBinary = true)) + else listOf(sourceRoot) + return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile) + */ + return null + } + + private fun modularBinaryRoot(root: VirtualFile): JavaModule? { + val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL + val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) } + + /* + val moduleInfoFile = + root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) + ?: root.takeIf { isJar }?.findFileByRelativePath(MULTI_RELEASE_MODULE_INFO_CLS_FILE)?.takeIf { + manifest?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) == true + } + + if (moduleInfoFile != null) { + val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null + return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile) + } + */ + + // Only .jar files can be automatic modules + if (isJar) { + val moduleRoot = listOf(JavaModule.Root(root, isBinary = true)) + + val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME) + if (automaticModuleName != null) { + return JavaModule.Automatic(automaticModuleName, moduleRoot) + } + + val originalFile = VfsUtilCore.virtualToIoFile(root) + val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension) + if (moduleName.isEmpty()) { + report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root) + return null + } + return JavaModule.Automatic(moduleName, moduleRoot) + } + + return null + } + + private fun readManifestAttributes(jarRoot: VirtualFile): Attributes? { + val manifestFile = jarRoot.findChild("META-INF")?.findChild("MANIFEST.MF") + return try { + manifestFile?.inputStream?.let(::Manifest)?.mainAttributes + } + catch (e: IOException) { + null + } + } + + private fun addModularRoots(modules: List, result: MutableList) { + // In current implementation, at most one source module is supported. This can be relaxed in the future if we support another + // compilation mode, similar to java's --module-source-path + val sourceModules = modules.filterIsInstance().filter(JavaModule::isSourceModule) + if (sourceModules.size > 1) { + for (module in sourceModules) { + report(ERROR, "Too many source module declarations found", module.moduleInfoFile) + } + return + } + + for (module in modules) { + val existing = javaModuleFinder.findModule(module.name) + if (existing == null) { + javaModuleFinder.addUserModule(module) + } + else if (module.moduleRoots != existing.moduleRoots) { + fun JavaModule.getRootFile() = + moduleRoots.firstOrNull()?.file?.let { VfsUtilCore.getVirtualFileForJar(it) ?: it } + + val thisFile = module.getRootFile() + val existingFile = existing.getRootFile() + val atExistingPath = if (existingFile == null) "" else " at: ${existingFile.path}" + report(STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " + + "has been found earlier on the module path$atExistingPath", thisFile) + } + } + + if (javaModuleFinder.allObservableModules.none()) return + + val sourceModule = sourceModules.singleOrNull() + val addAllModulePathToRoots = "ALL-MODULE-PATH" in additionalModules + if (addAllModulePathToRoots && sourceModule != null) { + report(ERROR, "-Xadd-modules=ALL-MODULE-PATH can only be used when compiling the unnamed module") + return + } + + val rootModules = when { + sourceModule != null -> listOf(sourceModule.name) + additionalModules + addAllModulePathToRoots -> modules.map(JavaModule::name) + else -> computeDefaultRootModules() + additionalModules + } + + val allDependencies = javaModuleGraph.getAllDependencies(rootModules) + if (allDependencies.any { moduleName -> javaModuleFinder.findModule(moduleName) is JavaModule.Automatic }) { + // According to java.lang.module javadoc, if at least one automatic module is added to the module graph, + // all observable automatic modules should be added. + // There are no automatic modules in the JDK, so we select all automatic modules out of user modules + for (module in modules) { + if (module is JavaModule.Automatic) { + allDependencies += module.name + } + } + } + + report(LOGGING, "Loading modules: $allDependencies") + + for (moduleName in allDependencies) { + val module = javaModuleFinder.findModule(moduleName) + if (module == null) { + report(ERROR, "Module $moduleName cannot be found in the module graph") + } + else { + for ((root, isBinary) in module.moduleRoots) { + result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE)) + } + } + } + + if (requireStdlibModule && sourceModule != null && !javaModuleGraph.reads(sourceModule.name, KOTLIN_STDLIB_MODULE_NAME)) { + report( + ERROR, + "The Kotlin standard library is not found in the module graph. " + + "Please ensure you have the 'requires $KOTLIN_STDLIB_MODULE_NAME' clause in your module definition", + sourceModule.moduleInfoFile + ) + } + } + + // See http://openjdk.java.net/jeps/261 + private fun computeDefaultRootModules(): List { + val result = arrayListOf() + + val systemModules = javaModuleFinder.systemModules.associateBy(JavaModule::name) + val javaSeExists = "java.se" in systemModules + if (javaSeExists) { + // The java.se module is a root, if it exists. + result.add("java.se") + } + + fun JavaModule.Explicit.exportsAtLeastOnePackageUnqualified(): Boolean = moduleInfo.exports.any { it.toModules.isEmpty() } + + if (!javaSeExists) { + // If it does not exist then every java.* module on the upgrade module path or among the system modules + // that exports at least one package, without qualification, is a root. + for ((name, module) in systemModules) { + if (name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) { + result.add(name) + } + } + } + + for ((name, module) in systemModules) { + // Every non-java.* module on the upgrade module path or among the system modules that exports at least one package, + // without qualification, is also a root. + if (!name.startsWith("java.") && module.exportsAtLeastOnePackageUnqualified()) { + result.add(name) + } + } + + return result + } + + private fun report(severity: CompilerMessageSeverity, message: String, file: VirtualFile? = null) { + if (messageCollector == null) { + throw IllegalStateException("${if (file != null) file.path + ":" else ""}$severity: $message (no MessageCollector configured)") + } + messageCollector.report( + severity, message, + if (file == null) null else CompilerMessageLocation.create(MessageUtil.virtualFileToPath(file)) + ) + } + + private companion object { + const val MULTI_RELEASE_MODULE_INFO_CLS_FILE = "META-INF/versions/9/${PsiJavaModule.MODULE_INFO_CLS_FILE}" + const val AUTOMATIC_MODULE_NAME = "Automatic-Module-Name" + const val IS_MULTI_RELEASE = "Multi-Release" + } +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt.as32 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt.as32 new file mode 100644 index 00000000000..d6c8a1b03ec --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt.as32 @@ -0,0 +1,302 @@ +/* + * Copyright 2010-2015 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.compiler + +import com.intellij.core.CoreJavaFileManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.* +import com.intellij.psi.impl.file.PsiPackageImpl +import com.intellij.psi.search.GlobalSearchScope +import gnu.trove.THashMap +import org.jetbrains.kotlin.cli.jvm.index.JavaRoot +import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex +import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex +import org.jetbrains.kotlin.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.isNotTopLevelClass +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 org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* + +// TODO: do not inherit from CoreJavaFileManager to avoid accidental usage of its methods which do not use caches/indices +// Currently, the only relevant usage of this class as CoreJavaFileManager is at CoreJavaDirectoryService.getPackage, +// which is indirectly invoked from PsiPackage.getSubPackages +class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager { + private val perfCounter = PerformanceCounter.create("Find Java class") + private lateinit var index: JvmDependenciesIndex + private lateinit var singleJavaFileRootsIndex: SingleJavaFileRootsIndex + private lateinit var packagePartProviders: List + private val topLevelClassesCache: MutableMap = THashMap() + private val allScope = GlobalSearchScope.allScope(myPsiManager.project) + private var useFastClassFilesReading = false + + fun initialize( + index: JvmDependenciesIndex, + packagePartProviders: List, + singleJavaFileRootsIndex: SingleJavaFileRootsIndex, + useFastClassFilesReading: Boolean + ) { + this.index = index + this.packagePartProviders = packagePartProviders + this.singleJavaFileRootsIndex = singleJavaFileRootsIndex + this.useFastClassFilesReading = useFastClassFilesReading + } + + private fun findPsiClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass? = perfCounter.time { + findVirtualFileForTopLevelClass(classId, searchScope)?.findPsiClassInVirtualFile(classId.relativeClassName.asString()) + } + + private fun findVirtualFileForTopLevelClass(classId: ClassId, searchScope: GlobalSearchScope): VirtualFile? { + val relativeClassName = classId.relativeClassName.asString() + return topLevelClassesCache.getOrPut(classId.packageFqName.child(classId.relativeClassName.pathSegments().first())) { + index.findClass(classId) { dir, type -> + findVirtualFileGivenPackage(dir, relativeClassName, type) + } + ?: singleJavaFileRootsIndex.findJavaSourceClass(classId) + }?.takeIf { it in searchScope } + } + + private val binaryCache: MutableMap = THashMap() + private val signatureParsingComponent = + BinaryClassSignatureParser() + + override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? { + val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null + + if (useFastClassFilesReading && virtualFile.extension == "class") { + // We return all class files' names in the directory in knownClassNamesInPackage method, so one may request an inner class + return binaryCache.getOrPut(classId) { + // Note that currently we implicitly suppose that searchScope for binary classes is constant and we do not use it + // as a key in cache + // This is a true assumption by now since there are two search scopes in compiler: one for sources and another one for binary + // When it become wrong because we introduce the modules into CLI, it's worth to consider + // having different KotlinCliJavaFileManagerImpl's for different modules + val classContent = virtualFile.contentsToByteArray() + if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null + classId.outerClassId?.let { outerClassId -> + val outerClass = findClass(outerClassId, searchScope) + return@getOrPut outerClass?.findInnerClass(classId.shortClassName) + } + + val resolver = ClassifierResolutionContext { findClass(it, allScope) } + + BinaryJavaClass( + virtualFile, + classId.asSingleFqName(), + resolver, + signatureParsingComponent, + outerClass = null, + classContent = classContent + ) + } + } + + return virtualFile.findPsiClassInVirtualFile(classId.relativeClassName.asString())?.let(::JavaClassImpl) + } + + // this method is called from IDEA to resolve dependencies in Java code + // which supposedly shouldn't have errors so the dependencies exist in general + override fun findClass(qName: String, scope: GlobalSearchScope): PsiClass? { + // String cannot be reliably converted to ClassId because we don't know where the package name ends and class names begin. + // For example, if qName is "a.b.c.d.e", we should either look for a top level class "e" in the package "a.b.c.d", + // or, for example, for a nested class with the relative qualified name "c.d.e" in the package "a.b". + // Below, we start by looking for the top level class "e" in the package "a.b.c.d" first, then for the class "d.e" in the package + // "a.b.c", and so on, until we find something. Most classes are top level, so most of the times the search ends quickly + + forEachClassId(qName) { classId -> + findPsiClass(classId, scope)?.let { return it } + } + + return null + } + + private inline fun forEachClassId(fqName: String, block: (ClassId) -> Unit) { + var classId = fqName.toSafeTopLevelClassId() ?: return + + while (true) { + block(classId) + + val packageFqName = classId.packageFqName + if (packageFqName.isRoot) break + + classId = ClassId( + packageFqName.parent(), + FqName(packageFqName.shortName().asString() + "." + classId.relativeClassName.asString()), + false + ) + } + } + + override fun findClasses(qName: String, scope: GlobalSearchScope): Array = perfCounter.time { + val result = ArrayList(1) + forEachClassId(qName) { classId -> + val relativeClassName = classId.relativeClassName.asString() + index.traverseDirectoriesInPackage(classId.packageFqName) { dir, rootType -> + val psiClass = + findVirtualFileGivenPackage(dir, relativeClassName, rootType) + ?.takeIf { it in scope } + ?.findPsiClassInVirtualFile(relativeClassName) + if (psiClass != null) { + result.add(psiClass) + } + // traverse all + true + } + + result.addIfNotNull( + singleJavaFileRootsIndex.findJavaSourceClass(classId) + ?.takeIf { it in scope } + ?.findPsiClassInVirtualFile(relativeClassName) + ) + + if (result.isNotEmpty()) { + return@time result.toTypedArray() + } + } + + PsiClass.EMPTY_ARRAY + } + + override fun findPackage(packageName: String): PsiPackage? { + var found = false + val packageFqName = packageName.toSafeFqName() ?: return null + index.traverseDirectoriesInPackage(packageFqName) { _, _ -> + found = true + //abort on first found + false + } + if (!found) { + found = packagePartProviders.any { it.findPackageParts(packageName).isNotEmpty() } + } + if (!found) { + found = singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName).isNotEmpty() + } + return if (found) PsiPackageImpl(myPsiManager, packageName) else null + } + + private fun findVirtualFileGivenPackage( + packageDir: VirtualFile, + classNameWithInnerClasses: String, + rootType: JavaRoot.RootType + ): VirtualFile? { + val topLevelClassName = classNameWithInnerClasses.substringBefore('.') + + val vFile = when (rootType) { + JavaRoot.RootType.BINARY -> packageDir.findChild("$topLevelClassName.class") + JavaRoot.RootType.SOURCE -> packageDir.findChild("$topLevelClassName.java") + } ?: return null + + if (!vFile.isValid) { + LOG.error("Invalid child of valid parent: ${vFile.path}; ${packageDir.isValid} path=${packageDir.path}") + return null + } + + return vFile + } + + private fun VirtualFile.findPsiClassInVirtualFile( + classNameWithInnerClasses: String + ): PsiClass? { + val file = myPsiManager.findFile(this) as? PsiClassOwner ?: return null + return findClassInPsiFile(classNameWithInnerClasses, file) + } + + override fun knownClassNamesInPackage(packageFqName: FqName): Set { + val result = hashSetOf() + index.traverseDirectoriesInPackage(packageFqName, continueSearch = { + dir, _ -> + + for (child in dir.children) { + if (child.extension == "class" || child.extension == "java") { + result.add(child.nameWithoutExtension) + } + } + + true + }) + + for (classId in singleJavaFileRootsIndex.findJavaSourceClasses(packageFqName)) { + assert(!classId.isNestedClass) { "ClassId of a single .java source class should not be nested: $classId" } + result.add(classId.shortClassName.asString()) + } + + return result + } + +/* + override fun findModules(moduleName: String, scope: GlobalSearchScope): Collection { + // TODO + return emptySet() + } +*/ + + override fun getNonTrivialPackagePrefixes(): Collection = emptyList() + + companion object { + private val LOG = Logger.getInstance(KotlinCliJavaFileManagerImpl::class.java) + + private fun findClassInPsiFile(classNameWithInnerClassesDotSeparated: String, file: PsiClassOwner): PsiClass? { + for (topLevelClass in file.classes) { + val candidate = findClassByTopLevelClass(classNameWithInnerClassesDotSeparated, topLevelClass) + if (candidate != null) { + return candidate + } + } + return null + } + + private fun findClassByTopLevelClass(className: String, topLevelClass: PsiClass): PsiClass? { + if (className.indexOf('.') < 0) { + return if (className == topLevelClass.name) topLevelClass else null + } + + val segments = StringUtil.split(className, ".").iterator() + if (!segments.hasNext() || segments.next() != topLevelClass.name) { + return null + } + var curClass = topLevelClass + while (segments.hasNext()) { + val innerClassName = segments.next() + val innerClass = curClass.findInnerClassByName(innerClassName, false) ?: return null + curClass = innerClass + } + return curClass + } + } +} + +// a sad workaround to avoid throwing exception when called from inside IDEA code +private fun safely(compute: () -> T): T? = try { + compute() +} +catch (e: IllegalArgumentException) { + null +} +catch (e: AssertionError) { + null +} + +private fun String.toSafeFqName(): FqName? = safely { FqName(this) } +private fun String.toSafeTopLevelClassId(): ClassId? = safely { ClassId.topLevel(FqName(this)) } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt.as32 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt.as32 new file mode 100644 index 00000000000..243a22cd8f6 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt.as32 @@ -0,0 +1,484 @@ +/* + * 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.compiler + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +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.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics +import org.jetbrains.kotlin.backend.common.output.OutputFileCollection +import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection +import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage +import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil +import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler +import org.jetbrains.kotlin.cli.jvm.config.* +import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.idea.MainFunctionDetector +import org.jetbrains.kotlin.javac.JavacWrapper +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager +import org.jetbrains.kotlin.modules.Module +import org.jetbrains.kotlin.modules.TargetId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.script.tryConstructClassFromStringArgs +import org.jetbrains.kotlin.util.PerformanceCounter +import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize +import java.io.File +import java.lang.reflect.InvocationTargetException +import java.net.URLClassLoader +import java.util.concurrent.TimeUnit + +object KotlinToJVMBytecodeCompiler { + + private fun getAbsolutePaths(buildFile: File, module: Module): List { + return module.getSourceFiles().map { sourceFile -> + val source = File(sourceFile) + if (!source.isAbsolute) { + File(buildFile.absoluteFile.parentFile, sourceFile).absolutePath + } + else { + source.absolutePath + } + } + } + + private fun writeOutput( + configuration: CompilerConfiguration, + outputFiles: OutputFileCollection, + mainClass: FqName? + ) { + val reportOutputFiles = configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES) + val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR) + val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) + if (jarPath != null) { + val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false) + CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, mainClass, outputFiles) + if (reportOutputFiles) { + val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath) + messageCollector.report(OUTPUT, message) + } + return + } + + val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".") + outputFiles.writeAll(outputDir, messageCollector, reportOutputFiles) + } + + private fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback { + if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) { + return GenerationStateEventCallback.DO_NOTHING + } + return GenerationStateEventCallback { state -> + val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput) + writeOutput(configuration, currentOutput, mainClass = null) + if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) { + state.factory.releaseGeneratedOutput() + } + } + } + + internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File, chunk: List): Boolean { + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + + val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project) + + val projectConfiguration = environment.configuration + for (module in chunk) { + moduleVisibilityManager.addModule(module) + } + + val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS) + for (path in friendPaths) { + moduleVisibilityManager.addFriendPath(path) + } + + val targetDescription = "in targets [" + chunk.joinToString { input -> input.getModuleName() + "-" + input.getModuleType() } + "]" + + val result = repeatAnalysisIfNeeded(analyze(environment, targetDescription), environment, targetDescription) + if (result == null || !result.shouldGenerateCode) return false + + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + + result.throwIfError() + + val outputs = newLinkedHashMapWithExpectedSize(chunk.size) + + for (module in chunk) { + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + val ktFiles = CompileEnvironmentUtil.getKtFiles( + environment.project, getAbsolutePaths(buildFile, module), projectConfiguration + ) { path -> throw IllegalStateException("Should have been checked before: $path") } + if (!checkKotlinPackageUsage(environment, ktFiles)) return false + + val moduleConfiguration = projectConfiguration.copy().apply { + put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory())) + } + + outputs[module] = generate(environment, moduleConfiguration, result, ktFiles, module) + } + + try { + for ((_, state) in outputs) { + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + writeOutput(state.configuration, state.factory, null) + } + + if (projectConfiguration.getBoolean(JVMConfigurationKeys.COMPILE_JAVA)) { + val singleModule = chunk.singleOrNull() + if (singleModule != null) { + return JavacWrapper.getInstance(environment.project).use { + it.compile(File(singleModule.getOutputDirectory())) + } + } + else { + projectConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).let { + it.report(WARNING, "A chunk contains multiple modules (${chunk.joinToString { it.getModuleName() }}). -Xuse-javac option couldn't be used to compile java files") + } + JavacWrapper.getInstance(environment.project).close() + } + } + + return true + } + finally { + outputs.values.forEach(GenerationState::destroy) + } + } + + internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List, buildFile: File) { + for (module in chunk) { + configuration.addKotlinSourceRoots(getAbsolutePaths(buildFile, module)) + } + + for (module in chunk) { + for ((path, packagePrefix) in module.getJavaSourceRoots()) { + configuration.addJavaSourceRoot(File(path), packagePrefix) + } + } + + val isJava9Module = false /*chunk.any { module -> + module.getJavaSourceRoots().any { (path, packagePrefix) -> + val file = File(path) + packagePrefix == null && + (file.name == PsiJavaModule.MODULE_INFO_FILE || + (file.isDirectory && file.listFiles().any { it.name == PsiJavaModule.MODULE_INFO_FILE })) + } + }*/ + + for (module in chunk) { + for (classpathRoot in module.getClasspathRoots()) { + configuration.add( + JVMConfigurationKeys.CONTENT_ROOTS, + if (isJava9Module) JvmModulePathRoot(File(classpathRoot)) else JvmClasspathRoot(File(classpathRoot)) + ) + } + } + + for (module in chunk) { + val modularJdkRoot = module.modularJdkRoot + if (modularJdkRoot != null) { + // We use the SDK of the first module in the chunk, which is not always correct because some other module in the chunk + // might depend on a different SDK + configuration.put(JVMConfigurationKeys.JDK_HOME, File(modularJdkRoot)) + break + } + } + + configuration.addAll(JVMConfigurationKeys.MODULES, chunk) + } + + private fun findMainClass(generationState: GenerationState, files: List): FqName? { + val mainFunctionDetector = MainFunctionDetector(generationState.bindingContext) + return files.asSequence() + .map { file -> + if (mainFunctionDetector.hasMain(file.declarations)) + JvmFileClassUtil.getFileClassInfoNoResolve(file).facadeClassFqName + else + null + } + .singleOrNull { it != null } + } + + fun compileBunchOfSources(environment: KotlinCoreEnvironment): Boolean { + val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project) + + val friendPaths = environment.configuration.getList(JVMConfigurationKeys.FRIEND_PATHS) + for (path in friendPaths) { + moduleVisibilityManager.addFriendPath(path) + } + + if (!checkKotlinPackageUsage(environment, environment.getSourceFiles())) return false + + val generationState = analyzeAndGenerate(environment) ?: return false + + val mainClass = findMainClass(generationState, environment.getSourceFiles()) + + try { + writeOutput(environment.configuration, generationState.factory, mainClass) + return true + } + finally { + generationState.destroy() + } + } + + internal fun compileAndExecuteScript(environment: KotlinCoreEnvironment, scriptArgs: List): ExitCode { + val scriptClass = compileScript(environment) ?: return ExitCode.COMPILATION_ERROR + + try { + try { + tryConstructClassFromStringArgs(scriptClass, scriptArgs) + ?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n") + } + finally { + // NB: these lines are required (see KT-9546) but aren't covered by tests + System.out.flush() + System.err.flush() + } + } + catch (e: Throwable) { + reportExceptionFromScript(e) + return ExitCode.SCRIPT_EXECUTION_ERROR + } + + return ExitCode.OK + } + + private fun repeatAnalysisIfNeeded( + result: AnalysisResult?, + environment: KotlinCoreEnvironment, + targetDescription: String? + ): AnalysisResult? { + if (result is AnalysisResult.RetryWithAdditionalJavaRoots) { + val configuration = environment.configuration + + val oldReadOnlyValue = configuration.isReadOnly + configuration.isReadOnly = false + configuration.addJavaSourceRoots(result.additionalJavaRoots) + configuration.isReadOnly = oldReadOnlyValue + + if (result.addToEnvironment) { + environment.updateClasspath(result.additionalJavaRoots.map { JavaSourceRoot(it, null) }) + } + + // Clear package caches (see KotlinJavaPsiFacade) + ApplicationManager.getApplication().runWriteAction { + (PsiManager.getInstance(environment.project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter() + } + + // Clear all diagnostic messages + configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]?.clear() + + // Repeat analysis with additional Java roots (kapt generated sources) + return analyze(environment, targetDescription) + } + + return result + } + + private fun reportExceptionFromScript(exception: Throwable) { + // expecting InvocationTargetException from constructor invocation with cause that describes the actual cause + val stream = System.err + val cause = exception.cause + if (exception !is InvocationTargetException || cause == null) { + exception.printStackTrace(stream) + return + } + stream.println(cause) + val fullTrace = cause.stackTrace + for (i in 0 until fullTrace.size - exception.stackTrace.size) { + stream.println("\tat " + fullTrace[i]) + } + } + + fun compileScript(environment: KotlinCoreEnvironment, parentClassLoader: ClassLoader? = null): Class<*>? { + val state = analyzeAndGenerate(environment) ?: return null + + try { + val urls = environment.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS).mapNotNull { root -> + when (root) { + is JvmModulePathRoot -> root.file // TODO: only add required modules + is JvmClasspathRoot -> root.file + else -> null + } + }.map { it.toURI().toURL() } + + val classLoader = GeneratedClassLoader(state.factory, parentClassLoader ?: URLClassLoader(urls.toTypedArray(), null)) + + val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed") + return classLoader.loadClass(script.fqName.asString()) + } + catch (e: Exception) { + throw RuntimeException("Failed to evaluate script: " + e, e) + } + } + + fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? { + val result = repeatAnalysisIfNeeded(analyze(environment, null), environment, null) ?: return null + + if (!result.shouldGenerateCode) return null + + result.throwIfError() + + return generate(environment, environment.configuration, result, environment.getSourceFiles(), null) + } + + private fun analyze(environment: KotlinCoreEnvironment, targetDescription: String?): AnalysisResult? { + val sourceFiles = environment.getSourceFiles() + val collector = environment.messageCollector + + val analysisStart = PerformanceCounter.currentTime() + val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector, environment.configuration.languageVersionSettings) + analyzerWithCompilerReport.analyzeAndReport(sourceFiles) { + val project = environment.project + val moduleOutputs = environment.configuration.get(JVMConfigurationKeys.MODULES)?.mapNotNullTo(hashSetOf()) { module -> + environment.findLocalFile(module.getOutputDirectory()) + }.orEmpty() + val sourcesOnly = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles) + // 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)) + TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( + project, + sourceFiles, + NoScopeRecordCliBindingTrace(), + environment.configuration, + environment::createPackagePartProvider, + sourceModuleSearchScope = scope + ) + } + + val analysisNanos = PerformanceCounter.currentTime() - analysisStart + + val sourceLinesOfCode = environment.countLinesOfCode(sourceFiles) + val time = TimeUnit.NANOSECONDS.toMillis(analysisNanos) + val speed = sourceLinesOfCode.toFloat() * 1000 / time + + val message = "ANALYZE: ${sourceFiles.size} files ($sourceLinesOfCode lines) ${targetDescription ?: ""}" + + "in $time ms - ${"%.3f".format(speed)} loc/s" + + K2JVMCompiler.reportPerf(environment.configuration, message) + + val analysisResult = analyzerWithCompilerReport.analysisResult + + return if (!analyzerWithCompilerReport.hasErrors() || analysisResult is AnalysisResult.RetryWithAdditionalJavaRoots) + analysisResult + else + null + } + + class DirectoriesScope( + project: Project, + private val directories: Set + ) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) { + private val fileSystems = directories.mapTo(hashSetOf(), VirtualFile::getFileSystem) + + override fun contains(file: VirtualFile): Boolean { + if (file.fileSystem !in fileSystems) return false + + var parent: VirtualFile = file + while (true) { + if (parent in directories) return true + parent = parent.parent ?: return false + } + } + + override fun toString() = "All files under: $directories" + } + + private fun GenerationState.Builder.withModule(module: Module?) = + apply { + targetId(module?.let { TargetId(it) }) + moduleName(module?.getModuleName()) + outDirectory(module?.let { File(it.getOutputDirectory()) }) + } + + private fun generate( + environment: KotlinCoreEnvironment, + configuration: CompilerConfiguration, + result: AnalysisResult, + sourceFiles: List, + module: Module? + ): GenerationState { + val isKapt2Enabled = environment.project.getUserData(IS_KAPT2_ENABLED_KEY) ?: false + val generationState = GenerationState.Builder( + environment.project, + ClassBuilderFactories.binaries(isKapt2Enabled), + result.moduleDescriptor, + result.bindingContext, + sourceFiles, + configuration + ) + .codegenFactory(if (configuration.getBoolean(JVMConfigurationKeys.IR)) JvmIrCodegenFactory else DefaultCodegenFactory) + .withModule(module) + .onIndependentPartCompilationEnd(createOutputFilesFlushingCallbackIfPossible(configuration)) + .build() + + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + + val generationStart = PerformanceCounter.currentTime() + + KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION) + + val generationNanos = PerformanceCounter.currentTime() - generationStart + val desc = if (module != null) "target " + module.getModuleName() + "-" + module.getModuleType() + " " else "" + val numberOfSourceFiles = sourceFiles.size + val numberOfLines = environment.countLinesOfCode(sourceFiles) + val time = TimeUnit.NANOSECONDS.toMillis(generationNanos) + val speed = numberOfLines.toFloat() * 1000 / time + val message = "GENERATE: $numberOfSourceFiles files ($numberOfLines lines) ${desc}in $time ms - ${"%.3f".format(speed)} loc/s" + + K2JVMCompiler.reportPerf(environment.configuration, message) + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + + AnalyzerWithCompilerReport.reportDiagnostics( + FilteredJvmDiagnostics( + generationState.collectedExtraJvmDiagnostics, + result.bindingContext.diagnostics + ), + environment.messageCollector + ) + + AnalyzerWithCompilerReport.reportBytecodeVersionErrors( + generationState.extraJvmDiagnosticsTrace.bindingContext, environment.messageCollector + ) + + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() + return generationState + } + + private val KotlinCoreEnvironment.messageCollector: MessageCollector + get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt.as32 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt.as32 new file mode 100644 index 00000000000..a52e80c4ef3 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/modules/CliJavaModuleFinder.kt.as32 @@ -0,0 +1,48 @@ +/* + * 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.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder + +class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder { + private val modulesRoot = jrtFileSystemRoot?.findChild("modules") + private val userModules = linkedMapOf() + + fun addUserModule(module: JavaModule) { + userModules.putIfAbsent(module.name, module) + } + + val allObservableModules: Sequence + get() = systemModules + userModules.values + + val systemModules: Sequence + get() = modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) + + override fun findModule(name: String): JavaModule? = + modulesRoot?.findChild(name)?.let(this::findSystemModule) ?: userModules[name] + + private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? { + /* + val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null + val moduleInfo = JavaModuleInfo.read(file) ?: return null + return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file) + */ + return null + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt.as32 b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt.as32 new file mode 100644 index 00000000000..716f1c7e740 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt.as32 @@ -0,0 +1,133 @@ +/* + * 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.load.java.structure.impl + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiTypeParameter +import com.intellij.psi.search.SearchScope +import org.jetbrains.kotlin.asJava.KtLightClassMarker +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.psiUtil.contains + +class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl(psiClass), VirtualFileBoundJavaClass, JavaAnnotationOwnerImpl, JavaModifierListOwnerImpl { + init { + assert(psiClass !is PsiTypeParameter) { "PsiTypeParameter should be wrapped in JavaTypeParameter, not JavaClass: use JavaClassifier.create()" } + } + + override val innerClassNames: Collection + get() = psi.innerClasses.mapNotNull { it.name?.takeIf(Name::isValidIdentifier)?.let(Name::identifier) } + + override fun findInnerClass(name: Name): JavaClass? { + return psi.findInnerClassByName(name.asString(), false)?.let(::JavaClassImpl) + } + + override val fqName: FqName? + get() { + val qualifiedName = psi.qualifiedName + return if (qualifiedName == null) null else FqName(qualifiedName) + } + + override val name: Name + get() = KtPsiUtil.safeName(psi.name) + + override val isInterface: Boolean + get() = psi.isInterface + + override val isAnnotationType: Boolean + get() = psi.isAnnotationType + + override val isEnum: Boolean + get() = psi.isEnum + + override val outerClass: JavaClassImpl? + get() { + val outer = psi.containingClass + return if (outer == null) null else JavaClassImpl(outer) + } + + override val typeParameters: List + get() = typeParameters(psi.typeParameters) + + override val supertypes: Collection + get() = classifierTypes(psi.superTypes) + + override val methods: Collection + get() { + assertNotLightClass() + // We apply distinct here because PsiClass#getMethods() can return duplicate PSI methods, for example in Lombok (see KT-11778) + // Return type seems to be null for example for the 'clone' Groovy method generated by @AutoClone (see EA-73795) + return methods(psi.methods.filter { method -> !method.isConstructor && method.returnType != null }).distinct() + } + + override val fields: Collection + get() { + assertNotLightClass() + return fields(psi.fields.filter { field -> + val name = field.name + // ex. Android plugin generates LightFields for resources started from '.' (.DS_Store file etc) + name != null && Name.isValidIdentifier(name) + }) + } + + override val constructors: Collection + get() { + assertNotLightClass() + // See for example org.jetbrains.plugins.scala.lang.psi.light.ScFunctionWrapper, + // which is present in getConstructors(), but its isConstructor() returns false + return constructors(psi.constructors.filter { method -> method.isConstructor }) + } + + override val isAbstract: Boolean + get() = JavaElementUtil.isAbstract(this) + + override val isStatic: Boolean + get() = JavaElementUtil.isStatic(this) + + override val isFinal: Boolean + get() = JavaElementUtil.isFinal(this) + + override val visibility: Visibility + get() = JavaElementUtil.getVisibility(this) + + override val lightClassOriginKind: LightClassOriginKind? + get() = (psi as? KtLightClassMarker)?.originKind + + override val virtualFile: VirtualFile? + get() = psi.containingFile?.virtualFile + + override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = psi.containingFile in scope + + override fun getAnnotationOwnerPsi() = psi.modifierList + + private fun assertNotLightClass() { + val psiClass = psi + if (psiClass !is KtLightClassMarker) return + + val message = "Querying members of JavaClass created for $psiClass of type ${psiClass::class.java} defined in file ${psiClass.containingFile?.virtualFile?.canonicalPath}" + LOGGER.error(message) + } + + companion object { + private val LOGGER = Logger.getInstance(JavaClassImpl::class.java) + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 new file mode 100644 index 00000000000..3ed195c944c --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt.as32 @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2015 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.load.kotlin + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.util.Computable +import com.intellij.openapi.vfs.VirtualFile + +class KotlinBinaryClassCache : Disposable { + private class RequestCache { + internal var virtualFile: VirtualFile? = null + internal var modificationStamp: Long = 0 + internal var virtualFileKotlinClass: VirtualFileKotlinClass? = null + + fun cache(file: VirtualFile, aClass: VirtualFileKotlinClass?): VirtualFileKotlinClass? { + virtualFile = file + virtualFileKotlinClass = aClass + modificationStamp = file.modificationStamp + + return aClass + } + } + + private val cache = object : ThreadLocal() { + override fun initialValue(): RequestCache { + return RequestCache() + } + } + + override fun dispose() { + // This is only relevant for tests. We create a new instance of Application for each test, and so a new instance of this service is + // also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this + // thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError + cache.remove() + } + + companion object { + fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? { + if (file.fileType !== JavaClassFileType.INSTANCE) return null + + val service = ServiceManager.getService(KotlinBinaryClassCache::class.java) + val requestCache = service.cache.get() + + if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) { + return requestCache.virtualFileKotlinClass + } + + val aClass = ApplicationManager.getApplication().runReadAction(Computable { + @Suppress("DEPRECATION") + VirtualFileKotlinClass.create(file, fileContent) + }) + + return requestCache.cache(file, aClass) + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleInfo.kt.as32 b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleInfo.kt.as32 new file mode 100644 index 00000000000..1bd0dfffc2d --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/modules/JavaModuleInfo.kt.as32 @@ -0,0 +1,89 @@ +/* + * 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.vfs.VirtualFile +//import com.intellij.psi.PsiJavaModule +import com.intellij.psi.PsiModifier +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.compact +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.ClassVisitor +//import org.jetbrains.org.objectweb.asm.ModuleVisitor +import org.jetbrains.org.objectweb.asm.Opcodes +//import org.jetbrains.org.objectweb.asm.Opcodes.ACC_TRANSITIVE +import java.io.IOException + +class JavaModuleInfo( + val moduleName: String, + val requires: List, + val exports: List +) { + data class Requires(val moduleName: String, val isTransitive: Boolean) + + data class Exports(val packageFqName: FqName, val toModules: List) + + override fun toString(): String = + "Module $moduleName (${requires.size} requires, ${exports.size} exports)" + + companion object { + /*fun create(psiJavaModule: PsiJavaModule): JavaModuleInfo { + return JavaModuleInfo( + psiJavaModule.name, + psiJavaModule.requires.mapNotNull { statement -> + statement.moduleName?.let { moduleName -> + JavaModuleInfo.Requires(moduleName, statement.hasModifierProperty(PsiModifier.TRANSITIVE)) + } + }, + psiJavaModule.exports.mapNotNull { statement -> + statement.packageName?.let { packageName -> + JavaModuleInfo.Exports(FqName(packageName), statement.moduleNames) + } + } + ) + }*/ + + /*fun read(file: VirtualFile): JavaModuleInfo? { + val contents = try { file.contentsToByteArray() } catch (e: IOException) { return null } + + var moduleName: String? = null + val requires = arrayListOf() + val exports = arrayListOf() + + ClassReader(contents).accept(object : ClassVisitor(Opcodes.ASM6) { + override fun visitModule(name: String, access: Int, version: String?): ModuleVisitor { + moduleName = name + + return object : ModuleVisitor(Opcodes.ASM6) { + override fun visitRequire(module: String, access: Int, version: String?) { + requires.add(Requires(module, (access and ACC_TRANSITIVE) != 0)) + } + + override fun visitExport(packageFqName: String, access: Int, modules: Array?) { + // For some reason, '/' is the delimiter in packageFqName here + exports.add(Exports(FqName(packageFqName.replace('/', '.')), modules?.toList().orEmpty())) + } + } + } + }, ClassReader.SKIP_DEBUG or ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES) + + return if (moduleName != null) + JavaModuleInfo(moduleName!!, requires.compact(), exports.compact()) + else null + }*/ + } +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java.as32 b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java.as32 new file mode 100644 index 00000000000..7b30fd60c9e --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java.as32 @@ -0,0 +1,1206 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.ShutDownTracker; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.text.StringUtilRt; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.rt.execution.junit.FileComparisonFailure; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.testFramework.TestDataFile; +import com.intellij.util.containers.ContainerUtil; +import junit.framework.TestCase; +import kotlin.collections.CollectionsKt; +import kotlin.collections.SetsKt; +import kotlin.jvm.functions.Function1; +import kotlin.text.StringsKt; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; +import org.jetbrains.kotlin.analyzer.AnalysisResult; +import org.jetbrains.kotlin.builtins.DefaultBuiltIns; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.checkers.CompilerTestLanguageVersionSettings; +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys; +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.kotlin.cli.common.messages.MessageCollector; +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt; +import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime; +import org.jetbrains.kotlin.config.*; +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl; +import org.jetbrains.kotlin.diagnostics.Diagnostic; +import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.diagnostics.Severity; +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages; +import org.jetbrains.kotlin.idea.KotlinLanguage; +import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil; +import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.psi.KtExpression; +import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.psi.KtPsiFactoryKt; +import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.resolve.BindingTrace; +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics; +import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil; +import org.jetbrains.kotlin.storage.LockBasedStorageManager; +import org.jetbrains.kotlin.test.util.JetTestUtilsKt; +import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo; +import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice; +import org.jetbrains.kotlin.util.slicedMap.SlicedMap; +import org.jetbrains.kotlin.util.slicedMap.WritableSlice; +import org.jetbrains.kotlin.utils.ExceptionUtilsKt; +import org.jetbrains.kotlin.utils.PathUtil; +import org.junit.Assert; + +import javax.tools.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.jetbrains.kotlin.test.InTextDirectivesUtils.isCompatibleTarget; +import static org.jetbrains.kotlin.test.InTextDirectivesUtils.isDirectiveDefined; + +public class KotlinTestUtils { + public static String TEST_MODULE_NAME = "test-module"; + + public static final String TEST_GENERATOR_NAME = "org.jetbrains.kotlin.generators.tests.TestsPackage"; + public static final String PLEASE_REGENERATE_TESTS = "Please regenerate tests (GenerateTests.kt)"; + + public static final boolean RUN_IGNORED_TESTS_AS_REGULAR = + Boolean.getBoolean("org.jetbrains.kotlin.run.ignored.tests.as.regular"); + + private static final List filesToDelete = new ArrayList<>(); + + /** + * Syntax: + * + * // MODULE: name(dependency1, dependency2, ...) + * + * // FILE: name + * + * Several files may follow one module + */ + private static final String MODULE_DELIMITER = ",\\s*"; + + private static final Pattern FILE_OR_MODULE_PATTERN = Pattern.compile( + "(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*)?" + + "//\\s*FILE:\\s*(.*)$", Pattern.MULTILINE); + private static final Pattern DIRECTIVE_PATTERN = Pattern.compile("^//\\s*!([\\w_]+)(:\\s*(.*)$)?", Pattern.MULTILINE); + private static final Pattern LINE_SEPARATOR_PATTERN = Pattern.compile("\\r\\n|\\r|\\n"); + + public static final BindingTrace DUMMY_TRACE = new BindingTrace() { + @NotNull + @Override + public BindingContext getBindingContext() { + return new BindingContext() { + + @NotNull + @Override + public Diagnostics getDiagnostics() { + return Diagnostics.Companion.getEMPTY(); + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return DUMMY_TRACE.get(slice, key); + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + return DUMMY_TRACE.getKeys(slice); + } + + @NotNull + @TestOnly + @Override + public ImmutableMap getSliceContents(@NotNull ReadOnlySlice slice) { + return ImmutableMap.of(); + } + + @Nullable + @Override + public KotlinType getType(@NotNull KtExpression expression) { + return DUMMY_TRACE.getType(expression); + } + + @Override + public void addOwnDataTo(@NotNull BindingTrace trace, boolean commitDiagnostics) { + // do nothing + } + }; + } + + @Override + public void record(WritableSlice slice, K key, V value) { + } + + @Override + public void record(WritableSlice slice, K key) { + } + + @Override + @SuppressWarnings("unchecked") + public V get(ReadOnlySlice slice, K key) { + if (slice == BindingContext.PROCESSED) return (V) Boolean.FALSE; + return SlicedMap.DO_NOTHING.get(slice, key); + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + assert slice.isCollective(); + return Collections.emptySet(); + } + + @Nullable + @Override + public KotlinType getType(@NotNull KtExpression expression) { + KotlinTypeInfo typeInfo = get(BindingContext.EXPRESSION_TYPE_INFO, expression); + return typeInfo != null ? typeInfo.getType() : null; + } + + @Override + public void recordType(@NotNull KtExpression expression, @Nullable KotlinType type) { + } + + @Override + public void report(@NotNull Diagnostic diagnostic) { + if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) { + throw new IllegalStateException("Unresolved: " + diagnostic.getPsiElement().getText()); + } + } + + @Override + public boolean wantsDiagnostics() { + return false; + } + }; + + public static BindingTrace DUMMY_EXCEPTION_ON_ERROR_TRACE = new BindingTrace() { + @NotNull + @Override + public BindingContext getBindingContext() { + return new BindingContext() { + @NotNull + @Override + public Diagnostics getDiagnostics() { + throw new UnsupportedOperationException(); + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return DUMMY_EXCEPTION_ON_ERROR_TRACE.get(slice, key); + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + return DUMMY_EXCEPTION_ON_ERROR_TRACE.getKeys(slice); + } + + @NotNull + @TestOnly + @Override + public ImmutableMap getSliceContents(@NotNull ReadOnlySlice slice) { + return ImmutableMap.of(); + } + + @Nullable + @Override + public KotlinType getType(@NotNull KtExpression expression) { + return DUMMY_EXCEPTION_ON_ERROR_TRACE.getType(expression); + } + + @Override + public void addOwnDataTo(@NotNull BindingTrace trace, boolean commitDiagnostics) { + // do nothing + } + }; + } + + @Override + public void record(WritableSlice slice, K key, V value) { + } + + @Override + public void record(WritableSlice slice, K key) { + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return null; + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + assert slice.isCollective(); + return Collections.emptySet(); + } + + @Nullable + @Override + public KotlinType getType(@NotNull KtExpression expression) { + return null; + } + + @Override + public void recordType(@NotNull KtExpression expression, @Nullable KotlinType type) { + } + + @Override + public void report(@NotNull Diagnostic diagnostic) { + if (diagnostic.getSeverity() == Severity.ERROR) { + throw new IllegalStateException(DefaultErrorMessages.render(diagnostic)); + } + } + + @Override + public boolean wantsDiagnostics() { + return true; + } + }; + + // We suspect sequences of eight consecutive hexadecimal digits to be a package part hash code + private static final Pattern STRIP_PACKAGE_PART_HASH_PATTERN = Pattern.compile("\\$([0-9a-f]{8})"); + + private KotlinTestUtils() { + } + + @NotNull + public static AnalysisResult analyzeFile(@NotNull KtFile file, @NotNull KotlinCoreEnvironment environment) { + return JvmResolveUtil.analyze(file, environment); + } + + @NotNull + public static KotlinCoreEnvironment createEnvironmentWithMockJdkAndIdeaAnnotations(Disposable disposable) { + return createEnvironmentWithMockJdkAndIdeaAnnotations(disposable, ConfigurationKind.ALL); + } + + @NotNull + public static KotlinCoreEnvironment createEnvironmentWithMockJdkAndIdeaAnnotations(Disposable disposable, @NotNull ConfigurationKind configurationKind) { + return createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(disposable, configurationKind, TestJdkKind.MOCK_JDK); + } + + @NotNull + public static KotlinCoreEnvironment createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea( + @NotNull Disposable disposable, + @NotNull ConfigurationKind configurationKind, + @NotNull TestJdkKind jdkKind + ) { + return KotlinCoreEnvironment.createForTests( + disposable, newConfiguration(configurationKind, jdkKind, getAnnotationsJar()), EnvironmentConfigFiles.JVM_CONFIG_FILES + ); + } + + @NotNull + public static KotlinCoreEnvironment createEnvironmentWithFullJdkAndIdeaAnnotations(Disposable disposable) { + return createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(disposable, ConfigurationKind.ALL, TestJdkKind.FULL_JDK); + } + + @NotNull + public static String getTestDataPathBase() { + return getHomeDirectory() + "/compiler/testData"; + } + + private static String homeDir = computeHomeDirectory(); + + @NotNull + public static String getHomeDirectory() { + return homeDir; + } + + @NotNull + private static String computeHomeDirectory() { + String userDir = System.getProperty("user.dir"); + File dir = new File(userDir == null ? "." : userDir); + return FileUtil.toCanonicalPath(dir.getAbsolutePath()); + } + + public static File findMockJdkRtJar() { + return new File(getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar"); + } + + // Differs from common mock JDK only by one additional 'nonExistingMethod' in Collection and constructor from Double in Throwable + // It's needed to test the way we load additional built-ins members that neither in black nor white lists + public static File findMockJdkRtModified() { + return new File(getHomeDirectory(), "compiler/testData/mockJDKModified/rt.jar"); + } + + public static File findAndroidApiJar() { + String androidJarProp = System.getProperty("android.jar"); + File androidJarFile = androidJarProp == null ? null : new File(androidJarProp); + if (androidJarFile == null || !androidJarFile.isFile()) { + throw new RuntimeException( + "Unable to get a valid path from 'android.jar' property (" + + androidJarProp + + "), please point it to the 'android.jar' file location"); + } + return androidJarFile; + } + + @NotNull + public static File findAndroidSdk() { + String androidSdkProp = System.getProperty("android.sdk"); + File androidSdkDir = androidSdkProp == null ? null : new File(androidSdkProp); + if (androidSdkDir == null || !androidSdkDir.isDirectory()) { + throw new RuntimeException( + "Unable to get a valid path from 'android.sdk' property (" + + androidSdkProp + + "), please point it to the android SDK location"); + } + return androidSdkDir; + } + + public static String getAndroidSdkSystemIndependentPath() { + return com.intellij.util.PathUtil.toSystemIndependentName(findAndroidSdk().getAbsolutePath()); + } + + public static File getAnnotationsJar() { + return new File(getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/annotations.jar"); + } + + public static void mkdirs(@NotNull File file) { + if (file.isDirectory()) { + return; + } + if (!file.mkdirs()) { + if (file.exists()) { + throw new IllegalStateException("Failed to create " + file + ": file exists and not a directory"); + } + throw new IllegalStateException("Failed to create " + file); + } + } + + @NotNull + public static File tmpDirForTest(TestCase test) throws IOException { + File answer = normalizeFile(FileUtil.createTempDirectory(test.getClass().getSimpleName(), test.getName())); + deleteOnShutdown(answer); + return answer; + } + + @NotNull + public static File tmpDir(String name) throws IOException { + // We should use this form. otherwise directory will be deleted on each test. + File answer = normalizeFile(FileUtil.createTempDirectory(new File(System.getProperty("java.io.tmpdir")), name, "")); + deleteOnShutdown(answer); + return answer; + } + + private static File normalizeFile(File file) throws IOException { + // Get canonical file to be sure that it's the same as inside the compiler, + // for example, on Windows, if a canonical path contains any space from FileUtil.createTempDirectory we will get + // a File with short names (8.3) in its path and it will break some normalization passes in tests. + return file.getCanonicalFile(); + } + + private static void deleteOnShutdown(File file) { + if (filesToDelete.isEmpty()) { + ShutDownTracker.getInstance().registerShutdownTask(() -> { + for (File victim : filesToDelete) { + FileUtil.delete(victim); + } + }); + } + + filesToDelete.add(file); + } + + @NotNull + public static KtFile createFile(@NotNull @NonNls String name, @NotNull String text, @NotNull Project project) { + String shortName = name.substring(name.lastIndexOf('/') + 1); + shortName = shortName.substring(shortName.lastIndexOf('\\') + 1); + LightVirtualFile virtualFile = new LightVirtualFile(shortName, KotlinLanguage.INSTANCE, StringUtilRt.convertLineSeparators(text)) { + @NotNull + @Override + public String getPath() { + //TODO: patch LightVirtualFile + return "/" + name; + } + }; + + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + PsiFileFactoryImpl factory = (PsiFileFactoryImpl) PsiFileFactory.getInstance(project); + //noinspection ConstantConditions + return (KtFile) factory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false); + } + + public static String doLoadFile(String myFullDataPath, String name) throws IOException { + String fullName = myFullDataPath + File.separatorChar + name; + return doLoadFile(new File(fullName)); + } + + public static String doLoadFile(@NotNull File file) throws IOException { + try { + return FileUtil.loadFile(file, CharsetToolkit.UTF8, true); + } + catch (FileNotFoundException fileNotFoundException) { + /* + * Unfortunately, the FileNotFoundException will only show the relative path in it's exception message. + * This clarifies the exception by showing the full path. + */ + String messageWithFullPath = file.getAbsolutePath() + " (No such file or directory)"; + throw new IOException( + "Ensure you have your 'Working Directory' configured correctly as the root " + + "Kotlin project directory in your test configuration\n\t" + + messageWithFullPath, + fileNotFoundException); + } + } + + public static String getFilePath(File file) { + return FileUtil.toSystemIndependentName(file.getPath()); + } + + @NotNull + public static CompilerConfiguration newConfiguration() { + CompilerConfiguration configuration = new CompilerConfiguration(); + configuration.put(CommonConfigurationKeys.MODULE_NAME, TEST_MODULE_NAME); + + if ("true".equals(System.getProperty("kotlin.ni"))) { + // Enable new inference for tests which do not declare their own language version settings + CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, new CompilerTestLanguageVersionSettings( + Collections.emptyMap(), + LanguageVersionSettingsImpl.DEFAULT.getApiVersion(), + LanguageVersionSettingsImpl.DEFAULT.getLanguageVersion(), + Collections.emptyMap() + )); + } + + configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, new MessageCollector() { + @Override + public void clear() { + } + + @Override + public void report( + @NotNull CompilerMessageSeverity severity, @NotNull String message, @Nullable CompilerMessageLocation location + ) { + if (severity == CompilerMessageSeverity.ERROR) { + String prefix = location == null + ? "" + : "(" + location.getPath() + ":" + location.getLine() + ":" + location.getColumn() + ") "; + throw new AssertionError(prefix + message); + } + } + + @Override + public boolean hasErrors() { + return false; + } + }); + + return configuration; + } + + @NotNull + public static CompilerConfiguration newConfiguration( + @NotNull ConfigurationKind configurationKind, + @NotNull TestJdkKind jdkKind, + @NotNull File... extraClasspath + ) { + return newConfiguration(configurationKind, jdkKind, Arrays.asList(extraClasspath), Collections.emptyList()); + } + + @NotNull + public static CompilerConfiguration newConfiguration( + @NotNull ConfigurationKind configurationKind, + @NotNull TestJdkKind jdkKind, + @NotNull List classpath, + @NotNull List javaSource + ) { + CompilerConfiguration configuration = newConfiguration(); + JvmContentRootsKt.addJavaSourceRoots(configuration, javaSource); + if (jdkKind == TestJdkKind.MOCK_JDK) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, findMockJdkRtJar()); + configuration.put(JVMConfigurationKeys.NO_JDK, true); + } + else if (jdkKind == TestJdkKind.MODIFIED_MOCK_JDK) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, findMockJdkRtModified()); + configuration.put(JVMConfigurationKeys.NO_JDK, true); + } + else if (jdkKind == TestJdkKind.ANDROID_API) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, findAndroidApiJar()); + configuration.put(JVMConfigurationKeys.NO_JDK, true); + } + else if (jdkKind == TestJdkKind.FULL_JDK_6) { + String jdk6 = System.getenv("JDK_16"); + assert jdk6 != null : "Environment variable JDK_16 is not set"; + configuration.put(JVMConfigurationKeys.JDK_HOME, new File(jdk6)); + } + else if (jdkKind == TestJdkKind.FULL_JDK_9) { + configuration.put(JVMConfigurationKeys.JDK_HOME, getJdk9Home()); + } + else { + JvmContentRootsKt.addJvmClasspathRoots(configuration, PathUtil.getJdkClassesRootsFromCurrentJre()); + } + + if (configurationKind.getWithRuntime()) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.runtimeJarForTests()); + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.scriptRuntimeJarForTests()); + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.kotlinTestJarForTests()); + } + else if (configurationKind.getWithMockRuntime()) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.minimalRuntimeJarForTests()); + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.scriptRuntimeJarForTests()); + } + if (configurationKind.getWithReflection()) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.reflectJarForTests()); + } + + JvmContentRootsKt.addJvmClasspathRoots(configuration, classpath); + + return configuration; + } + + @NotNull + public static File getJdk9Home() { + String jdk9 = System.getenv("JDK_9"); + if (jdk9 == null) { + jdk9 = System.getenv("JDK_19"); + if (jdk9 == null) { + throw new AssertionError("Environment variable JDK_9 is not set!"); + } + } + return new File(jdk9); + } + + public static void resolveAllKotlinFiles(KotlinCoreEnvironment environment) throws IOException { + List paths = ContentRootsKt.getKotlinSourceRoots(environment.getConfiguration()); + if (paths.isEmpty()) return; + List ktFiles = new ArrayList<>(); + for (String path : paths) { + File file = new File(path); + if (file.isFile()) { + ktFiles.add(loadJetFile(environment.getProject(), file)); + } + else { + //noinspection ConstantConditions + for (File childFile : file.listFiles()) { + if (childFile.getName().endsWith(".kt") || childFile.getName().endsWith(".kts")) { + ktFiles.add(loadJetFile(environment.getProject(), childFile)); + } + } + } + } + JvmResolveUtil.analyze(ktFiles, environment); + } + + public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull Editor editor) { + String actualText = editor.getDocument().getText(); + String afterText = new StringBuilder(actualText).insert(editor.getCaretModel().getOffset(), "").toString(); + + assertEqualsToFile(expectedFile, afterText); + } + + public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual) { + assertEqualsToFile(expectedFile, actual, s -> s); + } + + public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual) { + assertEqualsToFile(message, expectedFile, actual, s -> s); + } + + public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual, @NotNull Function1 sanitizer) { + assertEqualsToFile("Actual data differs from file content", expectedFile, actual, sanitizer); + } + + public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual, @NotNull Function1 sanitizer) { + try { + String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim())); + + if (!expectedFile.exists()) { + FileUtil.writeToFile(expectedFile, actualText); + Assert.fail("Expected data file did not exist. Generating: " + expectedFile); + } + String expected = FileUtil.loadFile(expectedFile, CharsetToolkit.UTF8, true); + + String expectedText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim())); + + if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) { + throw new FileComparisonFailure(message + ": " + expectedFile.getName(), + expected, actual, expectedFile.getAbsolutePath()); + } + } + catch (IOException e) { + throw ExceptionUtilsKt.rethrow(e); + } + } + + public static boolean compileKotlinWithJava( + @NotNull List javaFiles, + @NotNull List ktFiles, + @NotNull File outDir, + @NotNull Disposable disposable, + @Nullable File javaErrorFile + ) throws IOException { + if (!ktFiles.isEmpty()) { + KotlinCoreEnvironment environment = createEnvironmentWithFullJdkAndIdeaAnnotations(disposable); + LoadDescriptorUtil.compileKotlinToDirAndGetModule(ktFiles, outDir, environment); + } + else { + boolean mkdirs = outDir.mkdirs(); + assert mkdirs : "Not created: " + outDir; + } + if (javaFiles.isEmpty()) return true; + + return compileJavaFiles(javaFiles, Arrays.asList( + "-classpath", outDir.getPath() + File.pathSeparator + ForTestCompileRuntime.runtimeJarForTests(), + "-d", outDir.getPath() + ), javaErrorFile); + } + + public interface TestFileFactory { + F createFile(@Nullable M module, @NotNull String fileName, @NotNull String text, @NotNull Map directives); + M createModule(@NotNull String name, @NotNull List dependencies, @NotNull List friends); + } + + public static abstract class TestFileFactoryNoModules implements TestFileFactory { + @Override + public final F createFile( + @Nullable Void module, + @NotNull String fileName, + @NotNull String text, + @NotNull Map directives + ) { + return create(fileName, text, directives); + } + + @NotNull + public abstract F create(@NotNull String fileName, @NotNull String text, @NotNull Map directives); + + @Override + public Void createModule(@NotNull String name, @NotNull List dependencies, @NotNull List friends) { + return null; + } + } + + @NotNull + public static List createTestFiles(@Nullable String testFileName, String expectedText, TestFileFactory factory) { + return createTestFiles(testFileName, expectedText, factory, false); + } + + @NotNull + public static List createTestFiles(String testFileName, String expectedText, TestFileFactory factory, + boolean preserveLocations) { + Map directives = parseDirectives(expectedText); + + List testFiles = Lists.newArrayList(); + Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText); + boolean hasModules = false; + if (!matcher.find()) { + assert testFileName != null : "testFileName should not be null if no FILE directive defined"; + // One file + testFiles.add(factory.createFile(null, testFileName, expectedText, directives)); + } + else { + int processedChars = 0; + M module = null; + // Many files + while (true) { + String moduleName = matcher.group(1); + String moduleDependencies = matcher.group(2); + String moduleFriends = matcher.group(3); + if (moduleName != null) { + moduleName = moduleName.trim(); + hasModules = true; + module = factory.createModule(moduleName, parseModuleList(moduleDependencies), parseModuleList(moduleFriends)); + } + + String fileName = matcher.group(4); + int start = processedChars; + + boolean nextFileExists = matcher.find(); + int end; + if (nextFileExists) { + end = matcher.start(); + } + else { + end = expectedText.length(); + } + String fileText = preserveLocations ? + substringKeepingLocations(expectedText, start, end) : + expectedText.substring(start,end); + processedChars = end; + + testFiles.add(factory.createFile(module, fileName, fileText, directives)); + + if (!nextFileExists) break; + } + assert processedChars == expectedText.length() : "Characters skipped from " + + processedChars + + " to " + + (expectedText.length() - 1); + } + + if (isDirectiveDefined(expectedText, "WITH_COROUTINES")) { + M supportModule = hasModules ? factory.createModule("support", Collections.emptyList(), Collections.emptyList()) : null; + testFiles.add(factory.createFile(supportModule, + "CoroutineUtil.kt", + "package helpers\n" + + "import kotlin.coroutines.experimental.*\n" + + "fun handleResultContinuation(x: (T) -> Unit): Continuation = object: Continuation {\n" + + " override val context = EmptyCoroutineContext\n" + + " override fun resumeWithException(exception: Throwable) {\n" + + " throw exception\n" + + " }\n" + + "\n" + + " override fun resume(data: T) = x(data)\n" + + "}\n" + + "\n" + + "fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation = object: Continuation {\n" + + " override val context = EmptyCoroutineContext\n" + + " override fun resumeWithException(exception: Throwable) {\n" + + " x(exception)\n" + + " }\n" + + "\n" + + " override fun resume(data: Any?) { }\n" + + "}\n" + + "\n" + + "open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation {\n" + + " companion object : EmptyContinuation()\n" + + " override fun resume(data: Any?) {}\n" + + " override fun resumeWithException(exception: Throwable) { throw exception }\n" + + "}", + directives + )); + } + + return testFiles; + } + + private static String substringKeepingLocations(String string, int start, int end) { + Matcher matcher = LINE_SEPARATOR_PATTERN.matcher(string); + StringBuilder prefix = new StringBuilder(); + int lastLineOffset = 0; + while (matcher.find()) { + if (matcher.end() > start) { + break; + } + + lastLineOffset = matcher.end(); + prefix.append('\n'); + } + + while (lastLineOffset++ < start) { + prefix.append(' '); + } + + return prefix + string.substring(start, end); + } + + private static List parseModuleList(@Nullable String dependencies) { + if (dependencies == null) return Collections.emptyList(); + return StringsKt.split(dependencies, Pattern.compile(MODULE_DELIMITER), 0); + } + + @NotNull + public static Map parseDirectives(String expectedText) { + Map directives = Maps.newHashMap(); + Matcher directiveMatcher = DIRECTIVE_PATTERN.matcher(expectedText); + int start = 0; + while (directiveMatcher.find()) { + if (directiveMatcher.start() != start) { + Assert.fail("Directives should only occur at the beginning of a file: " + directiveMatcher.group()); + } + String name = directiveMatcher.group(1); + String value = directiveMatcher.group(3); + String oldValue = directives.put(name, value); + Assert.assertNull("Directive overwritten: " + name + " old value: " + oldValue + " new value: " + value, oldValue); + start = directiveMatcher.end() + 1; + } + return directives; + } + + public static List loadBeforeAfterText(String filePath) { + String content; + + try { + content = FileUtil.loadFile(new File(filePath), true); + } + catch (IOException e) { + throw new RuntimeException(e); + } + + List files = createTestFiles("", content, new TestFileFactoryNoModules() { + @NotNull + @Override + public String create(@NotNull String fileName, @NotNull String text, @NotNull Map directives) { + int firstLineEnd = text.indexOf('\n'); + return StringUtil.trimTrailing(text.substring(firstLineEnd + 1)); + } + }); + + Assert.assertTrue("Exactly two files expected: ", files.size() == 2); + + return files; + } + + public static String getLastCommentedLines(@NotNull Document document) { + List resultLines = new ArrayList<>(); + for (int i = document.getLineCount() - 1; i >= 0; i--) { + int lineStart = document.getLineStartOffset(i); + int lineEnd = document.getLineEndOffset(i); + if (document.getCharsSequence().subSequence(lineStart, lineEnd).toString().trim().isEmpty()) { + continue; + } + + if ("//".equals(document.getCharsSequence().subSequence(lineStart, lineStart + 2).toString())) { + resultLines.add(document.getCharsSequence().subSequence(lineStart + 2, lineEnd)); + } + else { + break; + } + } + Collections.reverse(resultLines); + StringBuilder result = new StringBuilder(); + for (CharSequence line : resultLines) { + result.append(line).append("\n"); + } + result.delete(result.length() - 1, result.length()); + return result.toString(); + } + + public enum CommentType { + ALL, + LINE_COMMENT, + BLOCK_COMMENT + } + + @NotNull + public static String getLastCommentInFile(@NotNull KtFile file) { + return CollectionsKt.first(getLastCommentsInFile(file, CommentType.ALL, true)); + } + + @NotNull + public static List getLastCommentsInFile(@NotNull KtFile file, CommentType commentType, boolean assertMustExist) { + PsiElement lastChild = file.getLastChild(); + if (lastChild != null && lastChild.getNode().getElementType().equals(KtTokens.WHITE_SPACE)) { + lastChild = lastChild.getPrevSibling(); + } + assert lastChild != null; + + List comments = ContainerUtil.newArrayList(); + + while (true) { + if (lastChild.getNode().getElementType().equals(KtTokens.BLOCK_COMMENT)) { + if (commentType == CommentType.ALL || commentType == CommentType.BLOCK_COMMENT) { + String lastChildText = lastChild.getText(); + comments.add(lastChildText.substring(2, lastChildText.length() - 2).trim()); + } + } + else if (lastChild.getNode().getElementType().equals(KtTokens.EOL_COMMENT)) { + if (commentType == CommentType.ALL || commentType == CommentType.LINE_COMMENT) { + comments.add(lastChild.getText().substring(2).trim()); + } + } + else { + break; + } + + lastChild = lastChild.getPrevSibling(); + } + + if (comments.isEmpty() && assertMustExist) { + throw new AssertionError(String.format( + "Test file '%s' should end in a comment of type %s; last node was: %s", file.getName(), commentType, lastChild)); + } + + return comments; + } + + public static boolean compileJavaFiles(@NotNull Collection files, List options) throws IOException { + return compileJavaFiles(files, options, null); + } + + private static boolean compileJavaFiles(@NotNull Collection files, List options, @Nullable File javaErrorFile) throws IOException { + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>(); + try (StandardJavaFileManager fileManager = + javaCompiler.getStandardFileManager(diagnosticCollector, Locale.ENGLISH, Charset.forName("utf-8"))) { + Iterable javaFileObjectsFromFiles = fileManager.getJavaFileObjectsFromFiles(files); + + JavaCompiler.CompilationTask task = javaCompiler.getTask( + new StringWriter(), // do not write to System.err + fileManager, + diagnosticCollector, + options, + null, + javaFileObjectsFromFiles); + + Boolean success = task.call(); // do NOT inline this variable, call() should complete before errorsToString() + if (javaErrorFile == null || !javaErrorFile.exists()) { + Assert.assertTrue(errorsToString(diagnosticCollector, true), success); + } + else { + assertEqualsToFile(javaErrorFile, errorsToString(diagnosticCollector, false)); + } + return success; + } + } + + public static boolean compileJavaFilesExternallyWithJava9(@NotNull Collection files, @NotNull List options) { + List command = new ArrayList<>(); + command.add(new File(getJdk9Home(), "bin/javac").getPath()); + command.addAll(options); + for (File file : files) { + command.add(file.getPath()); + } + + try { + Process process = new ProcessBuilder().command(command).inheritIO().start(); + process.waitFor(); + return process.exitValue() == 0; + } + catch (Exception e) { + throw ExceptionUtilsKt.rethrow(e); + } + } + + @NotNull + private static String errorsToString(@NotNull DiagnosticCollector diagnosticCollector, boolean humanReadable) { + StringBuilder builder = new StringBuilder(); + for (javax.tools.Diagnostic diagnostic : diagnosticCollector.getDiagnostics()) { + if (diagnostic.getKind() != javax.tools.Diagnostic.Kind.ERROR) continue; + + if (humanReadable) { + builder.append(diagnostic).append("\n"); + } + else { + builder.append(new File(diagnostic.getSource().toUri()).getName()).append(":") + .append(diagnostic.getLineNumber()).append(":") + .append(diagnostic.getColumnNumber()).append(":") + .append(diagnostic.getCode()).append("\n"); + } + } + return builder.toString(); + } + + public static String navigationMetadata(@TestDataFile String testFile) { + return testFile; + } + + public static String getTestsRoot(@NotNull Class testCaseClass) { + TestMetadata testClassMetadata = testCaseClass.getAnnotation(TestMetadata.class); + Assert.assertNotNull("No metadata for class: " + testCaseClass, testClassMetadata); + return testClassMetadata.value(); + } + + /** + * @return test data file name specified in the metadata of test method + */ + @Nullable + public static String getTestDataFileName(@NotNull Class testCaseClass, @NotNull String testName) { + try { + Method method = testCaseClass.getDeclaredMethod(testName); + return getMethodMetadata(method); + } + catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public static void assertAllTestsPresentByMetadata( + @NotNull Class testCaseClass, + @NotNull File testDataDir, + @NotNull Pattern filenamePattern, + @NotNull TargetBackend targetBackend, + boolean recursive, + @NotNull String... excludeDirs + ) { + File rootFile = new File(getTestsRoot(testCaseClass)); + + Set filePaths = collectPathsMetadata(testCaseClass); + Set exclude = SetsKt.setOf(excludeDirs); + + File[] files = testDataDir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + if (recursive && containsTestData(file, filenamePattern) && !exclude.contains(file.getName())) { + assertTestClassPresentByMetadata(testCaseClass, file); + } + } + else if (filenamePattern.matcher(file.getName()).matches() && isCompatibleTarget(targetBackend, file)) { + assertFilePathPresent(file, rootFile, filePaths); + } + } + } + } + + public static void assertAllTestsPresentInSingleGeneratedClass( + @NotNull Class testCaseClass, + @NotNull File testDataDir, + @NotNull Pattern filenamePattern, + @NotNull TargetBackend targetBackend + ) { + File rootFile = new File(getTestsRoot(testCaseClass)); + + Set filePaths = collectPathsMetadata(testCaseClass); + + FileUtil.processFilesRecursively(testDataDir, file -> { + if (file.isFile() && filenamePattern.matcher(file.getName()).matches() && isCompatibleTarget(targetBackend, file)) { + assertFilePathPresent(file, rootFile, filePaths); + } + + return true; + }); + } + + private static void assertFilePathPresent(File file, File rootFile, Set filePaths) { + String path = FileUtil.getRelativePath(rootFile, file); + if (path != null) { + String relativePath = nameToCompare(path); + if (!filePaths.contains(relativePath)) { + Assert.fail("Test data file missing from the generated test class: " + file + "\n" + PLEASE_REGENERATE_TESTS); + } + } + } + + private static Set collectPathsMetadata(Class testCaseClass) { + return ContainerUtil.newHashSet(ContainerUtil.map(collectMethodsMetadata(testCaseClass), KotlinTestUtils::nameToCompare)); + } + + @Nullable + private static String getMethodMetadata(Method method) { + TestMetadata testMetadata = method.getAnnotation(TestMetadata.class); + return (testMetadata != null) ? testMetadata.value() : null; + } + + private static Set collectMethodsMetadata(Class testCaseClass) { + Set filePaths = Sets.newHashSet(); + for (Method method : testCaseClass.getDeclaredMethods()) { + String path = getMethodMetadata(method); + if (path != null) { + filePaths.add(path); + } + } + return filePaths; + } + + private static boolean containsTestData(File dir, Pattern filenamePattern) { + File[] files = dir.listFiles(); + assert files != null; + for (File file : files) { + if (file.isDirectory()) { + if (containsTestData(file, filenamePattern)) { + return true; + } + } + else { + if (filenamePattern.matcher(file.getName()).matches()) { + return true; + } + } + } + return false; + } + + private static void assertTestClassPresentByMetadata(@NotNull Class outerClass, @NotNull File testDataDir) { + for (Class nestedClass : outerClass.getDeclaredClasses()) { + TestMetadata testMetadata = nestedClass.getAnnotation(TestMetadata.class); + if (testMetadata != null && testMetadata.value().equals(getFilePath(testDataDir))) { + return; + } + } + Assert.fail("Test data directory missing from the generated test class: " + testDataDir + "\n" + PLEASE_REGENERATE_TESTS); + } + + @NotNull + public static KtFile loadJetFile(@NotNull Project project, @NotNull File ioFile) throws IOException { + String text = FileUtil.loadFile(ioFile, true); + return KtPsiFactoryKt.KtPsiFactory(project).createPhysicalFile(ioFile.getName(), text); + } + + @NotNull + public static List loadToJetFiles(@NotNull KotlinCoreEnvironment environment, @NotNull List files) throws IOException { + List jetFiles = Lists.newArrayList(); + for (File file : files) { + jetFiles.add(loadJetFile(environment.getProject(), file)); + } + return jetFiles; + } + + @NotNull + public static ModuleDescriptorImpl createEmptyModule() { + return createEmptyModule(""); + } + + @NotNull + public static ModuleDescriptorImpl createEmptyModule(@NotNull String name) { + return createEmptyModule(name, DefaultBuiltIns.getInstance()); + } + + @NotNull + public static ModuleDescriptorImpl createEmptyModule(@NotNull String name, @NotNull KotlinBuiltIns builtIns) { + return new ModuleDescriptorImpl(Name.special(name), LockBasedStorageManager.NO_LOCKS, builtIns); + } + + @NotNull + public static File replaceExtension(@NotNull File file, @Nullable String newExtension) { + return new File(file.getParentFile(), FileUtil.getNameWithoutExtension(file) + (newExtension == null ? "" : "." + newExtension)); + } + + @NotNull + public static String replaceHashWithStar(@NotNull String string) { + return replaceHash(string, "*"); + } + + public static String replaceHash(@NotNull String string, @NotNull String replacement) { + //TODO: hashes are still used in SamWrapperCodegen + Matcher matcher = STRIP_PACKAGE_PART_HASH_PATTERN.matcher(string); + if (matcher.find()) { + return matcher.replaceAll("\\$" + replacement); + } + return string; + } + + public static boolean isAllFilesPresentTest(String testName) { + //noinspection SpellCheckingInspection + return testName.toLowerCase().startsWith("allfilespresentin"); + } + + public static String nameToCompare(@NotNull String name) { + return (SystemInfo.isFileSystemCaseSensitive ? name : name.toLowerCase()).replace('\\', '/'); + } + + public static boolean isMultiExtensionName(@NotNull String name) { + int firstDotIndex = name.indexOf('.'); + if (firstDotIndex == -1) { + return false; + } + // Several extension if name contains another dot + return name.indexOf('.', firstDotIndex + 1) != -1; + } +} diff --git a/generators/build.gradle.kts.as32 b/generators/build.gradle.kts.as32 new file mode 100644 index 00000000000..bd432828ebf --- /dev/null +++ b/generators/build.gradle.kts.as32 @@ -0,0 +1,43 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compile(projectTests(":compiler:cli")) + compile(projectTests(":idea:idea-maven")) + compile(projectTests(":j2k")) + compile(projectTests(":idea:idea-android")) + compile(projectTests(":jps-plugin")) + compile(projectTests(":plugins:android-extensions-compiler")) + compile(projectTests(":plugins:android-extensions-ide")) + compile(projectTests(":plugins:android-extensions-jps")) + compile(projectTests(":kotlin-annotation-processing")) + compile(projectTests(":kotlin-allopen-compiler-plugin")) + compile(projectTests(":kotlin-noarg-compiler-plugin")) + compile(projectTests(":kotlin-sam-with-receiver-compiler-plugin")) + compile(projectTests(":generators:test-generator")) + // testCompileOnly(intellijDep("jps-build-test")) + testCompileOnly(project(":kotlin-reflect-api")) + testRuntime(intellijDep()) { includeJars("idea_rt") } + testRuntime(projectDist(":kotlin-reflect")) +} + +sourceSets { + "main" { } + "test" { projectDefault() } +} + +projectTest { + workingDir = rootDir +} + +val generateTests by generator("org.jetbrains.kotlin.generators.tests.GenerateTestsKt") + +val generateProtoBuf by generator("org.jetbrains.kotlin.generators.protobuf.GenerateProtoBufKt") +val generateProtoBufCompare by generator("org.jetbrains.kotlin.generators.protobuf.GenerateProtoBufCompare") + +val generateGradleOptions by generator("org.jetbrains.kotlin.generators.arguments.GenerateGradleOptionsKt") + +testsJar() diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as32 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as32 new file mode 100644 index 00000000000..5cc0e1ed1f2 --- /dev/null +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as32 @@ -0,0 +1,1132 @@ +/* + * 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.generators.tests + +import org.jetbrains.kotlin.AbstractDataFlowValueRenderingTest +import org.jetbrains.kotlin.addImport.AbstractAddImportTest +import org.jetbrains.kotlin.allopen.AbstractBytecodeListingTestForAllOpen +import org.jetbrains.kotlin.android.* +import org.jetbrains.kotlin.android.annotator.AbstractAndroidGutterIconTest +import org.jetbrains.kotlin.android.configure.AbstractConfigureProjectTest +import org.jetbrains.kotlin.android.folding.AbstractAndroidResourceFoldingTest +import org.jetbrains.kotlin.android.intention.AbstractAndroidIntentionTest +import org.jetbrains.kotlin.android.intention.AbstractAndroidResourceIntentionTest +import org.jetbrains.kotlin.android.parcel.AbstractParcelBytecodeListingTest +import org.jetbrains.kotlin.android.quickfix.AbstractAndroidLintQuickfixTest +import org.jetbrains.kotlin.android.quickfix.AbstractAndroidQuickFixMultiFileTest +import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidBoxTest +import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidBytecodeShapeTest +import org.jetbrains.kotlin.android.synthetic.test.AbstractAndroidSyntheticPropertyDescriptorTest +import org.jetbrains.kotlin.annotation.AbstractAnnotationProcessorBoxTest +import org.jetbrains.kotlin.checkers.AbstractJavaAgainstKotlinBinariesCheckerTest +import org.jetbrains.kotlin.checkers.AbstractJavaAgainstKotlinSourceCheckerTest +import org.jetbrains.kotlin.checkers.AbstractJsCheckerTest +import org.jetbrains.kotlin.checkers.AbstractPsiCheckerTest +import org.jetbrains.kotlin.findUsages.AbstractFindUsagesTest +import org.jetbrains.kotlin.findUsages.AbstractKotlinFindUsagesWithLibraryTest +import org.jetbrains.kotlin.formatter.AbstractFormatterTest +import org.jetbrains.kotlin.formatter.AbstractTypingIndentationTestBase +import org.jetbrains.kotlin.generators.tests.generator.TestGroup +import org.jetbrains.kotlin.generators.tests.generator.testGroup +import org.jetbrains.kotlin.generators.util.KT_OR_KTS +import org.jetbrains.kotlin.generators.util.KT_OR_KTS_WITHOUT_DOTS_IN_NAME +import org.jetbrains.kotlin.generators.util.KT_WITHOUT_DOTS_IN_NAME +import org.jetbrains.kotlin.idea.AbstractExpressionSelectionTest +import org.jetbrains.kotlin.idea.AbstractKotlinTypeAliasByExpansionShortNameIndexTest +import org.jetbrains.kotlin.idea.AbstractSmartSelectionTest +import org.jetbrains.kotlin.idea.actions.AbstractGotoTestOrCodeActionTest +import org.jetbrains.kotlin.idea.caches.resolve.AbstractIdeCompiledLightClassTest +import org.jetbrains.kotlin.idea.caches.resolve.AbstractIdeLightClassTest +import org.jetbrains.kotlin.idea.caches.resolve.AbstractMultiModuleLineMarkerTest +import org.jetbrains.kotlin.idea.codeInsight.* +import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractCodeInsightActionTest +import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateHashCodeAndEqualsActionTest +import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateTestSupportMethodActionTest +import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateToStringActionTest +import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractMoveLeftRightTest +import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractMoveStatementTest +import org.jetbrains.kotlin.idea.codeInsight.postfix.AbstractPostfixTemplateProviderTest +import org.jetbrains.kotlin.idea.codeInsight.surroundWith.AbstractSurroundWithTest +import org.jetbrains.kotlin.idea.codeInsight.unwrap.AbstractUnwrapRemoveTest +import org.jetbrains.kotlin.idea.completion.test.* +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractBasicCompletionHandlerTest +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionCharFilterTest +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractKeywordCompletionHandlerTest +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractSmartCompletionHandlerTest +import org.jetbrains.kotlin.idea.completion.test.weighers.AbstractBasicCompletionWeigherTest +import org.jetbrains.kotlin.idea.completion.test.weighers.AbstractSmartCompletionWeigherTest +import org.jetbrains.kotlin.idea.configuration.AbstractGradleConfigureProjectByChangingFileTest +import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest +import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralKotlinToKotlinCopyPasteTest +import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralTextToKotlinCopyPasteTest +import org.jetbrains.kotlin.idea.conversion.copy.AbstractTextJavaToKotlinCopyPasteConversionTest +import org.jetbrains.kotlin.idea.coverage.AbstractKotlinCoverageOutputFilesTest +import org.jetbrains.kotlin.idea.debugger.AbstractBeforeExtractFunctionInsertionTest +import org.jetbrains.kotlin.idea.debugger.AbstractKotlinSteppingTest +import org.jetbrains.kotlin.idea.debugger.AbstractPositionManagerTest +import org.jetbrains.kotlin.idea.debugger.AbstractSmartStepIntoTest +import org.jetbrains.kotlin.idea.debugger.evaluate.* +import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToDecompiledLibraryTest +import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToLibrarySourceTest +import org.jetbrains.kotlin.idea.decompiler.stubBuilder.AbstractClsStubBuilderTest +import org.jetbrains.kotlin.idea.decompiler.stubBuilder.AbstractLoadJavaClsStubTest +import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractCommonDecompiledTextFromJsMetadataTest +import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractCommonDecompiledTextTest +import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractJsDecompiledTextFromJsMetadataTest +import org.jetbrains.kotlin.idea.decompiler.textBuilder.AbstractJvmDecompiledTextTest +import org.jetbrains.kotlin.idea.editor.AbstractMultiLineStringIndentTest +import org.jetbrains.kotlin.idea.editor.backspaceHandler.AbstractBackspaceHandlerTest +import org.jetbrains.kotlin.idea.editor.quickDoc.AbstractQuickDocProviderTest +import org.jetbrains.kotlin.idea.filters.AbstractKotlinExceptionFilterTest +import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest +import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest +import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyWithLibTest +import org.jetbrains.kotlin.idea.highlighter.* +import org.jetbrains.kotlin.idea.imports.AbstractJsOptimizeImportsTest +import org.jetbrains.kotlin.idea.imports.AbstractJvmOptimizeImportsTest +import org.jetbrains.kotlin.idea.inspections.AbstractLocalInspectionTest +import org.jetbrains.kotlin.idea.inspections.AbstractMultiFileLocalInspectionTest +import org.jetbrains.kotlin.idea.intentions.AbstractConcatenatedStringGeneratorTest +import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest +import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest2 +import org.jetbrains.kotlin.idea.intentions.AbstractMultiFileIntentionTest +import org.jetbrains.kotlin.idea.intentions.declarations.AbstractJoinLinesTest +import org.jetbrains.kotlin.idea.internal.AbstractBytecodeToolWindowTest +import org.jetbrains.kotlin.idea.kdoc.AbstractKDocHighlightingTest +import org.jetbrains.kotlin.idea.kdoc.AbstractKDocTypingTest +import org.jetbrains.kotlin.idea.navigation.* +import org.jetbrains.kotlin.idea.parameterInfo.AbstractParameterInfoTest +import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixMultiFileTest +import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixMultiModuleTest +import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixTest +import org.jetbrains.kotlin.idea.refactoring.AbstractNameSuggestionProviderTest +import org.jetbrains.kotlin.idea.refactoring.copy.AbstractCopyTest +import org.jetbrains.kotlin.idea.refactoring.copy.AbstractMultiModuleCopyTest +import org.jetbrains.kotlin.idea.refactoring.inline.AbstractInlineTest +import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractExtractionTest +import org.jetbrains.kotlin.idea.refactoring.move.AbstractMoveTest +import org.jetbrains.kotlin.idea.refactoring.move.AbstractMultiModuleMoveTest +import org.jetbrains.kotlin.idea.refactoring.pullUp.AbstractPullUpTest +import org.jetbrains.kotlin.idea.refactoring.pushDown.AbstractPushDownTest +import org.jetbrains.kotlin.idea.refactoring.rename.AbstractMultiModuleRenameTest +import org.jetbrains.kotlin.idea.refactoring.rename.AbstractRenameTest +import org.jetbrains.kotlin.idea.refactoring.safeDelete.AbstractMultiModuleSafeDeleteTest +import org.jetbrains.kotlin.idea.refactoring.safeDelete.AbstractSafeDeleteTest +import org.jetbrains.kotlin.idea.repl.AbstractIdeReplCompletionTest +import org.jetbrains.kotlin.idea.resolve.* +import org.jetbrains.kotlin.idea.scratch.AbstractScratchRunActionTest +import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationCompletionTest +import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest +import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest +import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest +import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest +import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest +import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest +import org.jetbrains.kotlin.idea.stubs.AbstractMultiFileHighlightingTest +import org.jetbrains.kotlin.idea.stubs.AbstractResolveByStubTest +import org.jetbrains.kotlin.idea.stubs.AbstractStubBuilderTest +import org.jetbrains.kotlin.incremental.* +import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterForWebDemoTest +import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterMultiFileTest +import org.jetbrains.kotlin.j2k.AbstractJavaToKotlinConverterSingleFileTest +//import org.jetbrains.kotlin.jps.build.* +//import org.jetbrains.kotlin.jps.build.android.AbstractAndroidJpsTestCase +//import org.jetbrains.kotlin.jps.incremental.AbstractJsProtoComparisonTest +//import org.jetbrains.kotlin.jps.incremental.AbstractJvmProtoComparisonTest +import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTest +import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest +import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg +import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg +import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest +import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest +import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest +import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest +import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest +import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest +import org.jetbrains.kotlin.test.TargetBackend + +fun main(args: Array) { + System.setProperty("java.awt.headless", "true") + + testGroup("idea/tests", "idea/testData") { + testClass { + model("resolve/additionalLazyResolve") + } + + testClass { + model("resolve/partialBodyResolve") + } + + testClass { + model("checker", recursive = false) + model("checker/regression") + model("checker/recovery") + model("checker/rendering") + model("checker/scripts", extension = "kts") + model("checker/duplicateJvmSignature") + model("checker/infos", testMethod = "doTestWithInfos") + model("checker/diagnosticsMessage") + } + + testClass { + model("kotlinAndJavaChecker/javaAgainstKotlin") + model("kotlinAndJavaChecker/javaWithKotlin") + } + + testClass { + model("kotlinAndJavaChecker/javaAgainstKotlin") + } + + testClass { + model("unifier") + } + + testClass { + model("checker/codeFragments", extension = "kt", recursive = false) + model("checker/codeFragments/imports", testMethod = "doTestWithImport", extension = "kt") + } + + testClass { + model("quickfix.special/codeFragmentAutoImport", extension = "kt", recursive = false) + } + + testClass { + model("checker/js") + } + + testClass { + model("quickfix", pattern = "^([\\w\\-_]+)\\.kt$", filenameStartsLowerCase = true) + } + + testClass { + model("navigation/gotoSuper", extension = "test", recursive = false) + } + + testClass { + model("navigation/gotoTypeDeclaration", extension = "test") + } + + testClass { + model("navigation/gotoDeclaration", extension = "test") + } + + testClass { + model("parameterInfo", recursive = true, excludeDirs = listOf("withLib1/sharedLib", "withLib2/sharedLib", "withLib3/sharedLib")) + } + + testClass { + model("navigation/gotoClass", testMethod = "doClassTest") + model("navigation/gotoSymbol", testMethod = "doSymbolTest") + } + + testClass { + model("decompiler/navigation/usercode") + model("decompiler/navigation/usercode", testClassName ="UsercodeWithJSModule", testMethod = "doWithJSModuleTest") + } + + testClass { + model("decompiler/navigation/usercode") + } + + testClass { + model("navigation/implementations", recursive = false) + } + + testClass { + model("navigation/gotoTestOrCode", pattern = "^(.+)\\.main\\..+\$") + } + + testClass { + model("search/inheritance") + } + + testClass { + model("search/annotations") + } + + testClass { + model("quickfix", pattern = """^(\w+)\.((before\.Main\.\w+)|(test))$""", testMethod = "doTestWithExtraFile") + } + + testClass { + model("typealiasExpansionIndex") + } + + testClass { + model("highlighter") + } + + testClass { + model("dslHighlighter") + } + + testClass { + model("usageHighlighter") + } + + testClass { + model("folding/noCollapse") + model("folding/checkCollapse", testMethod = "doSettingsFoldingTest") + } + + testClass { + model("codeInsight/surroundWith/if", testMethod = "doTestWithIfSurrounder") + model("codeInsight/surroundWith/ifElse", testMethod = "doTestWithIfElseSurrounder") + model("codeInsight/surroundWith/ifElseExpression", testMethod = "doTestWithIfElseExpressionSurrounder") + model("codeInsight/surroundWith/ifElseExpressionBraces", testMethod = "doTestWithIfElseExpressionBracesSurrounder") + model("codeInsight/surroundWith/not", testMethod = "doTestWithNotSurrounder") + model("codeInsight/surroundWith/parentheses", testMethod = "doTestWithParenthesesSurrounder") + model("codeInsight/surroundWith/stringTemplate", testMethod = "doTestWithStringTemplateSurrounder") + model("codeInsight/surroundWith/when", testMethod = "doTestWithWhenSurrounder") + model("codeInsight/surroundWith/tryCatch", testMethod = "doTestWithTryCatchSurrounder") + model("codeInsight/surroundWith/tryCatchExpression", testMethod = "doTestWithTryCatchExpressionSurrounder") + model("codeInsight/surroundWith/tryCatchFinally", testMethod = "doTestWithTryCatchFinallySurrounder") + model("codeInsight/surroundWith/tryCatchFinallyExpression", testMethod = "doTestWithTryCatchFinallyExpressionSurrounder") + model("codeInsight/surroundWith/tryFinally", testMethod = "doTestWithTryFinallySurrounder") + model("codeInsight/surroundWith/functionLiteral", testMethod = "doTestWithFunctionLiteralSurrounder") + model("codeInsight/surroundWith/withIfExpression", testMethod = "doTestWithSurroundWithIfExpression") + model("codeInsight/surroundWith/withIfElseExpression", testMethod = "doTestWithSurroundWithIfElseExpression") + } + + testClass { + model("joinLines") + } + + testClass { + model("codeInsight/breadcrumbs") + } + + testClass { + model("intentions", pattern = "^([\\w\\-_]+)\\.(kt|kts)$") + } + + testClass { + model("intentions/loopToCallChain", pattern = "^([\\w\\-_]+)\\.kt$") + } + + testClass { + model("concatenatedStringGenerator", pattern = "^([\\w\\-_]+)\\.kt$") + } + + testClass { + model("intentions", pattern = "^(inspections\\.test)$", singleClass = true) + model("inspections", pattern = "^(inspections\\.test)$", singleClass = true) + model("inspectionsLocal", pattern = "^(inspections\\.test)$", singleClass = true) + } + + testClass { + model("inspectionsLocal", pattern = "^([\\w\\-_]+)\\.(kt|kts)$") + } + + testClass { + model("hierarchy/class/type", extension = null, recursive = false, testMethod = "doTypeClassHierarchyTest") + model("hierarchy/class/super", extension = null, recursive = false, testMethod = "doSuperClassHierarchyTest") + model("hierarchy/class/sub", extension = null, recursive = false, testMethod = "doSubClassHierarchyTest") + model("hierarchy/calls/callers", extension = null, recursive = false, testMethod = "doCallerHierarchyTest") + model("hierarchy/calls/callersJava", extension = null, recursive = false, testMethod = "doCallerJavaHierarchyTest") + model("hierarchy/calls/callees", extension = null, recursive = false, testMethod = "doCalleeHierarchyTest") + model("hierarchy/overrides", extension = null, recursive = false, testMethod = "doOverrideHierarchyTest") + } + + testClass { + model("hierarchy/withLib", extension = null, recursive = false) + } + + testClass { + model("codeInsight/moveUpDown/classBodyDeclarations", pattern = KT_OR_KTS, testMethod = "doTestClassBodyDeclaration") + model("codeInsight/moveUpDown/closingBraces", testMethod = "doTestExpression") + model("codeInsight/moveUpDown/expressions", pattern = KT_OR_KTS, testMethod = "doTestExpression") + model("codeInsight/moveUpDown/parametersAndArguments", testMethod = "doTestExpression") + } + + testClass { + model("codeInsight/moveLeftRight") + } + + testClass { + model("refactoring/inline", pattern = "^(\\w+)\\.kt$") + } + + testClass { + model("codeInsight/unwrapAndRemove/removeExpression", testMethod = "doTestExpressionRemover") + model("codeInsight/unwrapAndRemove/unwrapThen", testMethod = "doTestThenUnwrapper") + model("codeInsight/unwrapAndRemove/unwrapElse", testMethod = "doTestElseUnwrapper") + model("codeInsight/unwrapAndRemove/removeElse", testMethod = "doTestElseRemover") + model("codeInsight/unwrapAndRemove/unwrapLoop", testMethod = "doTestLoopUnwrapper") + model("codeInsight/unwrapAndRemove/unwrapTry", testMethod = "doTestTryUnwrapper") + model("codeInsight/unwrapAndRemove/unwrapCatch", testMethod = "doTestCatchUnwrapper") + model("codeInsight/unwrapAndRemove/removeCatch", testMethod = "doTestCatchRemover") + model("codeInsight/unwrapAndRemove/unwrapFinally", testMethod = "doTestFinallyUnwrapper") + model("codeInsight/unwrapAndRemove/removeFinally", testMethod = "doTestFinallyRemover") + model("codeInsight/unwrapAndRemove/unwrapLambda", testMethod = "doTestLambdaUnwrapper") + model("codeInsight/unwrapAndRemove/unwrapFunctionParameter", testMethod = "doTestFunctionParameterUnwrapper") + } + + testClass { + model("codeInsight/expressionType") + } + + testClass { + model("editor/backspaceHandler") + } + + testClass { + model("editor/enterHandler/multilineString") + } + + testClass { + model("editor/quickDoc", pattern = """^([^_]+)\.[^\.]*$""") + } + + testClass { + model("refactoring/safeDelete/deleteClass/kotlinClass", testMethod = "doClassTest") + model("refactoring/safeDelete/deleteClass/kotlinClassWithJava", testMethod = "doClassTestWithJava") + model("refactoring/safeDelete/deleteClass/javaClassWithKotlin", extension = "java", testMethod = "doJavaClassTest") + model("refactoring/safeDelete/deleteObject/kotlinObject", testMethod = "doObjectTest") + model("refactoring/safeDelete/deleteFunction/kotlinFunction", testMethod = "doFunctionTest") + model("refactoring/safeDelete/deleteFunction/kotlinFunctionWithJava", testMethod = "doFunctionTestWithJava") + model("refactoring/safeDelete/deleteFunction/javaFunctionWithKotlin", testMethod = "doJavaMethodTest") + model("refactoring/safeDelete/deleteProperty/kotlinProperty", testMethod = "doPropertyTest") + model("refactoring/safeDelete/deleteProperty/kotlinPropertyWithJava", testMethod = "doPropertyTestWithJava") + model("refactoring/safeDelete/deleteProperty/javaPropertyWithKotlin", testMethod = "doJavaPropertyTest") + model("refactoring/safeDelete/deleteTypeAlias/kotlinTypeAlias", testMethod = "doTypeAliasTest") + model("refactoring/safeDelete/deleteTypeParameter/kotlinTypeParameter", testMethod = "doTypeParameterTest") + model("refactoring/safeDelete/deleteTypeParameter/kotlinTypeParameterWithJava", testMethod = "doTypeParameterTestWithJava") + model("refactoring/safeDelete/deleteValueParameter/kotlinValueParameter", testMethod = "doValueParameterTest") + model("refactoring/safeDelete/deleteValueParameter/kotlinValueParameterWithJava", testMethod = "doValueParameterTestWithJava") + } + + testClass { + model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("resolve/referenceInJava/binaryAndSource", extension = "java") + model("resolve/referenceInJava/sourceOnly", extension = "java") + } + + testClass { + model("resolve/referenceInJava/binaryAndSource", extension = "java") + } + + testClass { + model("resolve/referenceWithLib", recursive = false) + } + + testClass { + model("resolve/referenceInLib", recursive = false) + } + + testClass { + model("resolve/referenceToJavaWithWrongFileStructure", recursive = false) + } + + testClass { + model("findUsages/kotlin", pattern = """^(.+)\.0\.(kt|kts)$""") + model("findUsages/java", pattern = """^(.+)\.0\.java$""") + model("findUsages/propertyFiles", pattern = """^(.+)\.0\.properties$""") + } + + testClass { + model("findUsages/libraryUsages", pattern = """^(.+)\.0\.kt$""") + } + + testClass { + model("refactoring/move", extension = "test", singleClass = true) + } + + testClass { + model("refactoring/copy", extension = "test", singleClass = true) + } + + testClass { + model("refactoring/moveMultiModule", extension = "test", singleClass = true) + } + + testClass { + model("refactoring/copyMultiModule", extension = "test", singleClass = true) + } + + testClass { + model("refactoring/safeDeleteMultiModule", extension = "test", singleClass = true) + } + + testClass { + model("multiFileIntentions", extension = "test", singleClass = true, filenameStartsLowerCase = true) + } + + testClass { + model("multiFileLocalInspections", extension = "test", singleClass = true, filenameStartsLowerCase = true) + } + + testClass { + model("multiFileInspections", extension = "test", singleClass = true) + } + + testClass { + model("formatter", pattern = """^([^\.]+)\.after\.kt.*$""") + model("formatter", pattern = """^([^\.]+)\.after\.inv\.kt.*$""", + testMethod = "doTestInverted", testClassName = "FormatterInverted") + } + + testClass { + model("indentationOnNewline", pattern = """^([^\.]+)\.after\.kt.*$""", testMethod = "doNewlineTest", + testClassName = "DirectSettings") + model("indentationOnNewline", pattern = """^([^\.]+)\.after\.inv\.kt.*$""", testMethod = "doNewlineTestWithInvert", + testClassName = "InvertedSettings") + } + + testClass { + model("diagnosticMessage", recursive = false) + } + + testClass { + model("diagnosticMessage/js", recursive = false, targetBackend = TargetBackend.JS) + } + + testClass { + model("refactoring/rename", extension = "test", singleClass = true) + } + + testClass { + model("refactoring/renameMultiModule", extension = "test", singleClass = true) + } + + testClass { + model("codeInsight/outOfBlock") + } + + testClass { + model("dataFlowValueRendering") + } + + testClass { + model("copyPaste/conversion", pattern = """^([^\.]+)\.java$""") + } + + testClass { + model("copyPaste/plainTextConversion", pattern = """^([^\.]+)\.txt$""") + } + + testClass { + model("copyPaste/plainTextLiteral", pattern = """^([^\.]+)\.txt$""") + } + + testClass { + model("copyPaste/literal", pattern = """^([^\.]+)\.kt$""") + } + + testClass { + model("copyPaste/imports", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doTestCopy", testClassName = "Copy", recursive = false) + model("copyPaste/imports", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doTestCut", testClassName = "Cut", recursive = false) + } + + testClass { + model("copyPaste/moveDeclarations", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doTest") + } + + testClass { + model("exitPoints") + } + + testClass { + model("codeInsight/lineMarker") + } + + testClass { + model("multiModuleLineMarker", extension = null, recursive = false) + } + + testClass { + model("shortenRefs", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + testClass { + model("addImport", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("smartSelection", testMethod = "doTestSmartSelection", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("structureView/fileStructure", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("expressionSelection", testMethod = "doTestExpressionSelection", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("decompiler/decompiledText", pattern = """^([^\.]+)$""") + } + + testClass { + model("decompiler/decompiledTextJvm", pattern = """^([^\.]+)$""") + } + + testClass { + model("decompiler/decompiledText", pattern = """^([^\.]+)$""", targetBackend = TargetBackend.JS) + } + + testClass { + model("decompiler/decompiledTextJs", pattern = """^([^\.]+)$""", targetBackend = TargetBackend.JS) + } + + testClass { + model("decompiler/stubBuilder", extension = null, recursive = false) + } + + testClass { + model("editor/optimizeImports/jvm", pattern = KT_WITHOUT_DOTS_IN_NAME) + model("editor/optimizeImports/common", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + testClass { + model("editor/optimizeImports/js", pattern = KT_WITHOUT_DOTS_IN_NAME) + model("editor/optimizeImports/common", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("debugger/positionManager", recursive = false, extension = "kt", testClassName = "SingleFile") + model("debugger/positionManager", recursive = false, extension = null, testClassName = "MultiFile") + } + + testClass { + model("debugger/exceptionFilter", pattern = """^([^\.]+)$""", recursive = false) + } + + testClass { + model("debugger/smartStepInto") + } + + testClass { + model("debugger/insertBeforeExtractFunction", extension = "kt") + } + + testClass { + model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest", testClassName = "StepInto") + model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doSmartStepIntoTest", testClassName = "SmartStepInto") + model("debugger/tinyApp/src/stepping/stepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest", testClassName = "StepIntoOnly") + model("debugger/tinyApp/src/stepping/stepOut", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOutTest") + model("debugger/tinyApp/src/stepping/stepOver", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOverTest") + model("debugger/tinyApp/src/stepping/stepOverForce", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOverForceTest") + model("debugger/tinyApp/src/stepping/filters", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest") + model("debugger/tinyApp/src/stepping/custom", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doCustomTest") + } + + testClass { + model("debugger/tinyApp/src/evaluate/singleBreakpoint", testMethod = "doSingleBreakpointTest") + model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest") + } + + testClass { + model("stubs", extension = "kt") + } + + testClass { + model("multiFileHighlighting", recursive = false) + } + + testClass { + model("multiModuleQuickFix", recursive = false, extension = null) + } + + testClass { + model("navigation/implementations/multiModule", recursive = false, extension = null) + } + + testClass { + model("navigation/relatedSymbols/multiModule", recursive = false, extension = null) + } + + testClass { + model("navigation/gotoSuper/multiModule", recursive = false, extension = null) + } + + testClass { + model("refactoring/introduceVariable", pattern = KT_OR_KTS, testMethod = "doIntroduceVariableTest") + model("refactoring/extractFunction", pattern = KT_OR_KTS, testMethod = "doExtractFunctionTest") + model("refactoring/introduceProperty", pattern = KT_OR_KTS, testMethod = "doIntroducePropertyTest") + model("refactoring/introduceParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceSimpleParameterTest") + model("refactoring/introduceLambdaParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceLambdaParameterTest") + model("refactoring/introduceJavaParameter", extension = "java", testMethod = "doIntroduceJavaParameterTest") + model("refactoring/introduceTypeParameter", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeParameterTest") + model("refactoring/introduceTypeAlias", pattern = KT_OR_KTS, testMethod = "doIntroduceTypeAliasTest") + model("refactoring/extractSuperclass", pattern = KT_OR_KTS, testMethod = "doExtractSuperclassTest") + model("refactoring/extractInterface", pattern = KT_OR_KTS, testMethod = "doExtractInterfaceTest") + } + + testClass { + model("refactoring/pullUp/k2k", extension = "kt", singleClass = true, testClassName = "K2K", testMethod = "doKotlinTest") + model("refactoring/pullUp/k2j", extension = "kt", singleClass = true, testClassName = "K2J", testMethod = "doKotlinTest") + model("refactoring/pullUp/j2k", extension = "java", singleClass = true, testClassName = "J2K", testMethod = "doJavaTest") + } + + testClass { + model("refactoring/pushDown/k2k", extension = "kt", singleClass = true, testClassName = "K2K", testMethod = "doKotlinTest") + model("refactoring/pushDown/k2j", extension = "kt", singleClass = true, testClassName = "K2J", testMethod = "doKotlinTest") + model("refactoring/pushDown/j2k", extension = "java", singleClass = true, testClassName = "J2K", testMethod = "doJavaTest") + } + + testClass { + model("debugger/selectExpression", recursive = false) + model("debugger/selectExpression/disallowMethodCalls", testMethod = "doTestWoMethodCalls") + } + + testClass { + model("coverage/outputFiles") + } + + testClass { + model("internal/toolWindow", recursive = false, extension = null) + } + + testClass("org.jetbrains.kotlin.idea.kdoc.KdocResolveTestGenerated") { + model("kdoc/resolve") + } + + testClass { + model("kdoc/highlighting") + } + + testClass { + model("kdoc/typing") + } + + testClass { + model("codeInsight/generate/testFrameworkSupport") + } + + testClass { + model("codeInsight/generate/equalsWithHashCode") + } + + testClass { + model("codeInsight/generate/secondaryConstructors") + } + + testClass { + model("codeInsight/generate/toString") + } + + testClass { + model("repl/completion") + } + + testClass { + model("codeInsight/postfix") + } + + testClass { + model("script/definition/highlighting", extension = null, recursive = false) + } + + testClass { + model("script/definition/navigation", extension = null, recursive = false) + } + + testClass { + model("script/definition/completion", extension = null, recursive = false) + } + + testClass { + model("refactoring/nameSuggestionProvider") + } + + testClass { + model("slicer", singleClass = true) + } + + testClass { + model("slicer/inflow", singleClass = true) + } + + testClass { + model("slicer/inflow", singleClass = true) + } + + testClass { + model("scratch", extension = "kts", testMethod = "doCompilingTest", testClassName = "Compiling", recursive = false) + model("scratch", extension = "kts", testMethod = "doReplTest", testClassName = "Repl", recursive = false) + model("scratch/multiFile", extension = null, testMethod = "doMultiFileTest", recursive = false) + } + } + + /* + // Maven and Gradle are not relevent for AS branch + + testGroup("idea/idea-maven/test", "idea/idea-maven/testData") { + testClass { + model("configurator/jvm", extension = null, recursive = false, testMethod = "doTestWithMaven") + model("configurator/js", extension = null, recursive = false, testMethod = "doTestWithJSMaven") + } + + testClass { + model("maven-inspections", pattern = "^([\\w\\-]+).xml$", singleClass = true) + } + } + + testGroup("idea/idea-gradle/tests", "idea/testData") { + testClass { + model("configuration/gradle", extension = null, recursive = false, testMethod = "doTestGradle") + model("configuration/gsk", extension = null, recursive = false, testMethod = "doTestGradle") + } + } + + */ + + testGroup("idea/tests", "compiler/testData") { + testClass { + model("loadJava/compiledKotlin") + } + + testClass { + model("loadJava/compiledKotlin", testMethod = "doTestCompiledKotlin") + } + + testClass { + model("asJava/lightClasses", excludeDirs = listOf("delegation"), pattern = KT_OR_KTS_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("asJava/lightClasses", excludeDirs = listOf("local", "compilationErrors", "ideRegression"), pattern = KT_OR_KTS_WITHOUT_DOTS_IN_NAME) + } + } + + testGroup("idea/idea-completion/tests", "idea/idea-completion/testData") { + testClass { + model("injava", extension = "java", recursive = false) + } + + testClass { + model("injava", extension = "java", recursive = false) + } + + testClass { + model("injava/stdlib", extension = "java", recursive = false) + } + + testClass { + model("weighers/basic", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("weighers/smart", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("basic/common") + model("basic/js") + } + + testClass { + model("basic/common") + model("basic/java") + } + + testClass { + model("smart") + } + + testClass { + model("keywords", recursive = false) + } + + testClass { + model("basic/withLib", recursive = false) + } + + testClass { + model("handlers/basic", pattern = KT_WITHOUT_DOTS_IN_NAME) + } + + testClass { + model("handlers/smart") + } + + testClass { + model("handlers/keywords") + } + + testClass { + model("handlers/charFilter") + } + + testClass { + model("basic/multifile", extension = null, recursive = false) + } + + testClass { + model("smartMultiFile", extension = null, recursive = false) + } + + testClass("KDocCompletionTestGenerated") { + model("kdoc") + } + + testClass { + model("basic/java8") + } + + testClass { + model("incrementalResolve") + } + + testClass { + model("multiPlatform", recursive = false, extension = null) + } + } + + //TODO: move these tests into idea-completion module + testGroup("idea/tests", "idea/idea-completion/testData") { + testClass { + model("handlers/runtimeCast") + } + + testClass { + model("basic/codeFragments", extension = "kt") + } + } + + testGroup("j2k/tests", "j2k/testData") { + testClass { + model("fileOrElement", extension = "java") + } + } + testGroup("j2k/tests", "j2k/testData") { + testClass { + model("multiFile", extension = null, recursive = false) + } + } + testGroup("j2k/tests", "j2k/testData") { + testClass { + model("fileOrElement", extension = "java") + } + } +/* There is no jps in AS + testGroup("jps-plugin/jps-tests/test", "jps-plugin/testData") { + testClass { + model("incremental/multiModule", extension = null, excludeParentDirs = true) + model("incremental/pureKotlin", extension = null, recursive = false) + model("incremental/withJava", extension = null, excludeParentDirs = true) + model("incremental/inlineFunCallSite", extension = null, excludeParentDirs = true) + model("incremental/classHierarchyAffected", extension = null, excludeParentDirs = true) + } + + testClass { + model("incremental/lookupTracker/jvm", extension = null, recursive = false) + } + testClass { + model("incremental/lookupTracker/js", extension = null, recursive = false) + } + + testClass { + model("incremental/lazyKotlinCaches", extension = null, excludeParentDirs = true) + model("incremental/changeIncrementalOption", extension = null, excludeParentDirs = true) + } + + testClass { + model("incremental/cacheVersionChanged", extension = null, excludeParentDirs = true) + } + + testClass { + model("incremental/cacheVersionChanged", extension = null, excludeParentDirs = true) + } + } + + testGroup("jps-plugin/jps-tests/test", "jps-plugin/testData") { + fun TestGroup.TestClass.commonProtoComparisonTests() { + model("comparison/classSignatureChange", extension = null, excludeParentDirs = true) + model("comparison/classPrivateOnlyChange", extension = null, excludeParentDirs = true) + model("comparison/classMembersOnlyChanged", extension = null, excludeParentDirs = true) + model("comparison/packageMembers", extension = null, excludeParentDirs = true) + model("comparison/unchanged", extension = null, excludeParentDirs = true) + } + + testClass { + commonProtoComparisonTests() + model("comparison/jvmOnly", extension = null, excludeParentDirs = true) + } + + testClass { + commonProtoComparisonTests() + model("comparison/jsOnly", extension = null, excludeParentDirs = true) + } + } +*/ + testGroup("compiler/incremental-compilation-impl/test", "jps-plugin/testData") { + testClass { + model("incremental/pureKotlin", extension = null, recursive = false) + model("incremental/classHierarchyAffected", extension = null, recursive = false) + model("incremental/inlineFunCallSite", extension = null, excludeParentDirs = true) + model("incremental/withJava", extension = null, excludeParentDirs = true) + model("incremental/incrementalJvmCompilerOnly", extension = null, excludeParentDirs = true) + } + + testClass { + model("incremental/pureKotlin", extension = null, recursive = false) + model("incremental/classHierarchyAffected", extension = null, recursive = false) + model("incremental/js", extension = null, excludeParentDirs = true) + } + + testClass { + model("incremental/js/friendsModuleDisabled", extension = null, recursive = false) + } + + testClass { + model("incremental/multiplatform", extension = null, excludeParentDirs = true) + } + testClass { + model("incremental/multiplatform", extension = null, excludeParentDirs = true) + } + } + + testGroup("plugins/android-extensions/android-extensions-compiler/test", "plugins/android-extensions/android-extensions-compiler/testData") { + testClass { + model("descriptors", recursive = false, extension = null) + } + + testClass { + model("codegen/android", recursive = false, extension = null, testMethod = "doCompileAgainstAndroidSdkTest") + model("codegen/android", recursive = false, extension = null, testMethod = "doFakeInvocationTest", testClassName = "Invoke") + } + + testClass { + model("codegen/bytecodeShape", recursive = false, extension = null) + } + + testClass { + model("parcel/codegen") + } + } + + testGroup("plugins/annotation-collector/test", "plugins/annotation-collector/testData") { + testClass { + model("collectToFile", recursive = false, extension = null) + } + } + + testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") { + testClass { + model("converter") + } + + testClass { + model("kotlinRunner") + } + } + + testGroup("plugins/allopen/allopen-cli/test", "plugins/allopen/allopen-cli/testData") { + testClass { + model("bytecodeListing", extension = "kt") + } + } + + testGroup("plugins/noarg/noarg-cli/test", "plugins/noarg/noarg-cli/testData") { + testClass { + model("bytecodeListing", extension = "kt") + } + + testClass { + model("box", targetBackend = TargetBackend.JVM) + } + } + + testGroup("plugins/sam-with-receiver/sam-with-receiver-cli/test", "plugins/sam-with-receiver/sam-with-receiver-cli/testData") { + testClass { + model("diagnostics") + } + testClass { + model("script", extension = "kts") + } + } + + testGroup("plugins/android-extensions/android-extensions-idea/tests", "plugins/android-extensions/android-extensions-idea/testData") { + testClass { + model("android/completion", recursive = false, extension = null) + } + + testClass { + model("android/goto", recursive = false, extension = null) + } + + testClass { + model("android/rename", recursive = false, extension = null) + } + + testClass { + model("android/renameLayout", recursive = false, extension = null) + } + + testClass { + model("android/findUsages", recursive = false, extension = null) + } + + testClass { + model("android/usageHighlighting", recursive = false, extension = null) + } + + testClass { + model("android/extraction", recursive = false, extension = null) + } + + testClass { + model("android/parcel/checker", excludeParentDirs = true) + } + + testClass { + model("android/parcel/quickfix", pattern = """^(\w+)\.((before\.Main\.\w+)|(test))$""", testMethod = "doTestWithExtraFile") + } + } + + testGroup("idea/idea-android/tests", "idea/testData") { + testClass { + model("configuration/android-gradle", pattern = """(\w+)_before\.gradle$""", testMethod = "doTestAndroidGradle") + model("configuration/android-gsk", pattern = """(\w+)_before\.gradle.kts$""", testMethod = "doTestAndroidGradle") + } + + testClass { + model("android/intention", pattern = "^([\\w\\-_]+)\\.kt$") + } + + testClass { + model("android/resourceIntention", extension = "test", singleClass = true) + } + + testClass { + model("android/quickfix", pattern = """^(\w+)\.((before\.Main\.\w+)|(test))$""", testMethod = "doTestWithExtraFile") + } + + testClass { + model("android/lintQuickfix", pattern = "^([\\w\\-_]+)\\.kt$") + } + + testClass { + model("android/folding") + } + + testClass { + model("android/gutterIcon") + } + } +/* + testGroup("plugins/android-extensions/android-extensions-jps/test", "plugins/android-extensions/android-extensions-jps/testData") { + testClass { + model("android", recursive = false, extension = null) + } + } +*/ +} diff --git a/idea-runner/build.gradle.kts.as32 b/idea-runner/build.gradle.kts.as32 new file mode 100644 index 00000000000..be7bfa48925 --- /dev/null +++ b/idea-runner/build.gradle.kts.as32 @@ -0,0 +1,23 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compileOnly(project(":idea")) + compileOnly(project(":idea:idea-maven")) + compileOnly(project(":idea:idea-gradle")) + compileOnly(project(":idea:idea-jvm")) + + compile(intellijDep()) + + runtimeOnly(files(toolsJar())) +} + +val ideaPluginDir: File by rootProject.extra +val ideaSandboxDir: File by rootProject.extra + +runIdeTask("runIde", ideaPluginDir, ideaSandboxDir) { + dependsOn(":dist", ":ideaPlugin") +} diff --git a/idea/build.gradle.kts.as32 b/idea/build.gradle.kts.as32 new file mode 100644 index 00000000000..d622c4789eb --- /dev/null +++ b/idea/build.gradle.kts.as32 @@ -0,0 +1,127 @@ +import org.gradle.jvm.tasks.Jar + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testRuntime(intellijDep()) + + compile(project(":kotlin-stdlib")) + compileOnly(project(":kotlin-reflect-api")) + compile(project(":core:descriptors")) + compile(project(":core:descriptors.jvm")) + compile(project(":compiler:backend")) + compile(project(":compiler:cli-common")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":compiler:frontend.script")) + compile(project(":js:js.frontend")) + compile(project(":js:js.serializer")) + compile(project(":compiler:light-classes")) + compile(project(":compiler:util")) + compile(project(":kotlin-build-common")) + compile(project(":compiler:daemon-common")) + compile(projectRuntimeJar(":kotlin-daemon-client")) + compile(project(":kotlin-compiler-runner")) { isTransitive = false } + compile(project(":compiler:plugin-api")) + compile(project(":eval4j")) + compile(project(":j2k")) + compile(project(":idea:formatter")) + compile(project(":idea:idea-core")) + compile(project(":idea:ide-common")) + compile(project(":idea:idea-jps-common")) + compile(project(":idea:kotlin-gradle-tooling")) + compile(project(":plugins:uast-kotlin")) + compile(project(":plugins:uast-kotlin-idea")) + compile(project(":kotlin-script-util")) { isTransitive = false } + + compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } + compile(commonDep("org.jetbrains", "markdown")) + + compileOnly(project(":kotlin-daemon-client")) + + compileOnly(intellijDep()) + compileOnly(commonDep("com.google.code.findbugs", "jsr305")) + compileOnly(intellijPluginDep("IntelliLang")) + compileOnly(intellijPluginDep("copyright")) + compileOnly(intellijPluginDep("properties")) + compileOnly(intellijPluginDep("java-i18n")) + + testCompile(project(":kotlin-test:kotlin-test-junit")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false } + testCompile(project(":idea:idea-jvm")) { isTransitive = false } + testCompile(project(":idea:idea-gradle")) { isTransitive = false } + testCompile(project(":idea:idea-maven")) { isTransitive = false } + testCompile(commonDep("junit:junit")) + + testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false } + testRuntime(projectDist(":kotlin-reflect")) + testRuntime(projectDist(":kotlin-preloader")) + + testCompile(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false } + + testRuntime(project(":plugins:android-extensions-compiler")) + testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false } + testRuntime(project(":allopen-ide-plugin")) { isTransitive = false } + testRuntime(project(":kotlin-allopen-compiler-plugin")) + testRuntime(project(":noarg-ide-plugin")) { isTransitive = false } + testRuntime(project(":kotlin-noarg-compiler-plugin")) + testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false } + testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false } + testRuntime(project(":idea:idea-android")) { isTransitive = false } + testRuntime(project(":plugins:lint")) { isTransitive = false } + testRuntime(project(":plugins:uast-kotlin")) + + (rootProject.extra["compilerModules"] as Array).forEach { + testRuntime(project(it)) + } + + testCompile(intellijPluginDep("IntelliLang")) + testCompile(intellijPluginDep("copyright")) + testCompile(intellijPluginDep("properties")) + testCompile(intellijPluginDep("java-i18n")) + testCompileOnly(intellijDep()) + testCompileOnly(commonDep("com.google.code.findbugs", "jsr305")) + testCompileOnly(intellijPluginDep("gradle")) + testCompileOnly(intellijPluginDep("Groovy")) + //testCompileOnly(intellijPluginDep("maven")) + + testRuntime(intellijPluginDep("junit")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("coverage")) + //testRuntime(intellijPluginDep("maven")) + testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) + testRuntime(intellijPluginDep("testng")) +} + +sourceSets { + "main" { + projectDefault() + java.srcDirs("idea-completion/src", + "idea-live-templates/src", + "idea-repl/src") + resources.srcDirs("idea-repl/src").apply { include("META-INF/**") } + } + "test" { + projectDefault() + java.srcDirs( + "idea-completion/tests", + "idea-live-templates/tests") + } +} + +projectTest { + dependsOn(":dist") + workingDir = rootDir +} + +testsJar {} + +classesDirsArtifact() +configureInstrumentation() + diff --git a/idea/idea-android/build.gradle.kts.as32 b/idea/idea-android/build.gradle.kts.as32 new file mode 100644 index 00000000000..fc654ca0a23 --- /dev/null +++ b/idea/idea-android/build.gradle.kts.as32 @@ -0,0 +1,74 @@ + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testRuntime(intellijDep()) + + compileOnly(project(":kotlin-reflect-api")) + compile(project(":compiler:util")) + compile(project(":compiler:light-classes")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":idea")) + compile(project(":idea:idea-jvm")) + compile(project(":idea:idea-core")) + compile(project(":idea:ide-common")) + compile(project(":idea:idea-gradle")) + + compile(androidDxJar()) + + compileOnly(project(":kotlin-android-extensions-runtime")) + compileOnly(intellijDep()) + compileOnly(intellijPluginDep("android")) + + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false } + testCompile(project(":plugins:lint")) { isTransitive = false } + testCompile(project(":idea:idea-jvm")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(projectTests(":idea")) + testCompile(projectTests(":idea:idea-gradle")) + testCompile(commonDep("junit:junit")) + + testCompile(intellijDep()) + testCompile(intellijPluginDep("properties")) + testCompileOnly(intellijPluginDep("android")) + + testRuntime(projectDist(":kotlin-reflect")) + testRuntime(project(":plugins:android-extensions-ide")) + testRuntime(project(":plugins:kapt3-idea")) + testRuntime(project(":sam-with-receiver-ide-plugin")) + testRuntime(project(":noarg-ide-plugin")) + testRuntime(project(":allopen-ide-plugin")) + + testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) + testRuntime(intellijPluginDep("copyright")) + testRuntime(intellijPluginDep("coverage")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("IntelliLang")) + testRuntime(intellijPluginDep("java-decompiler")) + testRuntime(intellijPluginDep("java-i18n")) + testRuntime(intellijPluginDep("junit")) + //testRuntime(intellijPluginDep("maven")) + testRuntime(intellijPluginDep("testng")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +projectTest { + workingDir = rootDir + useAndroidSdk() +} + +testsJar {} + diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/ConvertJavaToKotlinProviderImpl.kt.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/ConvertJavaToKotlinProviderImpl.kt.as32 new file mode 100644 index 00000000000..d3549c18501 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/ConvertJavaToKotlinProviderImpl.kt.as32 @@ -0,0 +1,43 @@ +/* + * 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.android + +import com.android.tools.idea.npw.template.ConvertJavaToKotlinProvider +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiJavaFile +import org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator +import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction +import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator +import org.jetbrains.kotlin.idea.configuration.getCanBeConfiguredModules +import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion + +class ConvertJavaToKotlinProviderImpl : ConvertJavaToKotlinProvider { + override fun configureKotlin(project: Project) { + val configurator = KotlinProjectConfigurator.EP_NAME.findExtension(KotlinAndroidGradleModuleConfigurator::class.java) + val nonConfiguredModules = getCanBeConfiguredModules(project, configurator) + configurator.configureSilently(project, nonConfiguredModules, bundledRuntimeVersion()) + } + + override fun getKotlinVersion(): String { + return bundledRuntimeVersion() + } + + override fun convertToKotlin(project: Project, files: List): List { + return JavaToKotlinAction.convertFiles(files, project, askExternalCodeProcessing = false) + } +} diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.as32 index 8bb7eb98cf4..5e466a01580 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.as32 +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.as32 @@ -25,7 +25,6 @@ import com.android.resources.ResourceType; import com.android.tools.idea.configurations.Configuration; import com.android.tools.idea.configurations.ConfigurationManager; import com.android.tools.idea.res.AppResourceRepository; -import com.android.tools.idea.res.LocalResourceRepository; import com.android.tools.idea.res.ResourceHelper; import com.android.tools.idea.ui.resourcechooser.ColorPicker; import com.android.utils.XmlUtils; diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleLibraryDataService.kt.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleLibraryDataService.kt.as32 new file mode 100644 index 00000000000..b48e72e6afd --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidGradleLibraryDataService.kt.as32 @@ -0,0 +1,64 @@ +/* + * 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.android.configure + +import com.android.tools.idea.gradle.project.model.JavaModuleModel +import com.android.tools.idea.gradle.project.sync.idea.data.service.AndroidProjectKeys +import com.android.tools.idea.io.FilePaths +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider +import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.impl.libraries.LibraryEx +import com.intellij.openapi.roots.libraries.Library +import org.jetbrains.kotlin.idea.configuration.detectPlatformByPlugin +import org.jetbrains.kotlin.idea.framework.detectLibraryKind +import org.jetbrains.kotlin.idea.framework.libraryKind +import java.io.File + +class KotlinAndroidGradleLibraryDataService : AbstractProjectDataService() { + override fun getTargetDataKey() = AndroidProjectKeys.JAVA_MODULE_MODEL + + override fun postProcess( + toImport: MutableCollection>, + projectData: ProjectData?, + project: Project, + modelsProvider: IdeModifiableModelsProvider + ) { + for (dataNode in toImport) { + val targetLibraryKind = detectPlatformByPlugin(dataNode.parent as DataNode)?.libraryKind + if (targetLibraryKind != null) { + for (dep in dataNode.data.jarLibraryDependencies) { + val library = modelsProvider.findLibraryByBinaryPath(dep.binaryPath) as LibraryEx? ?: continue + if (library.kind == null) { + val model = modelsProvider.getModifiableLibraryModel(library) as LibraryEx.ModifiableModelEx + detectLibraryKind(model.getFiles(OrderRootType.CLASSES))?.let { model.kind = it } + } + } + } + } + } + + private fun IdeModifiableModelsProvider.findLibraryByBinaryPath(path: File?): Library? { + if (path == null) return null + val url = FilePaths.pathToIdeaUrl(path) + return allLibraries.firstOrNull { url in getModifiableLibraryModel(it).getUrls(OrderRootType.CLASSES) } + } +} diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidModuleSetupStep.kt.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidModuleSetupStep.kt.as32 new file mode 100644 index 00000000000..b7b2f5caf11 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinAndroidModuleSetupStep.kt.as32 @@ -0,0 +1,40 @@ +/* + * 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.android.configure + +import com.android.tools.idea.gradle.project.model.AndroidModuleModel +import com.android.tools.idea.gradle.project.sync.setup.post.ModuleSetupStep +import com.intellij.openapi.module.Module +import com.intellij.openapi.progress.ProgressIndicator +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.idea.configuration.compilerArgumentsBySourceSet +import org.jetbrains.kotlin.idea.configuration.configureFacetByCompilerArguments +import org.jetbrains.kotlin.idea.configuration.sourceSetName +import org.jetbrains.kotlin.idea.facet.KotlinFacet + +class KotlinAndroidModuleSetupStep : ModuleSetupStep() { + override fun setUpModule(module: Module, progressIndicator: ProgressIndicator?) { + val facet = AndroidFacet.getInstance(module) ?: return + val androidModel = AndroidModuleModel.get(facet) ?: return + val sourceSetName = androidModel.selectedVariant.name + if (module.sourceSetName == sourceSetName) return + val argsInfo = module.compilerArgumentsBySourceSet?.get(sourceSetName) ?: return + val kotlinFacet = KotlinFacet.get(module) ?: return + module.sourceSetName = sourceSetName + configureFacetByCompilerArguments(kotlinFacet, argsInfo, null) + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinGradleAndroidModuleModelProjectDataService.kt.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinGradleAndroidModuleModelProjectDataService.kt.as32 new file mode 100644 index 00000000000..b060507a2eb --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/configure/KotlinGradleAndroidModuleModelProjectDataService.kt.as32 @@ -0,0 +1,54 @@ +/* + * 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.android.configure + +import com.android.tools.idea.gradle.project.model.AndroidModuleModel +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.Key +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider +import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler +import org.jetbrains.kotlin.idea.configuration.configureFacetByGradleModule + +class KotlinGradleAndroidModuleModelProjectDataService : AbstractProjectDataService() { + companion object { + val KEY = Key(AndroidModuleModel::class.qualifiedName!!, 0) + } + + override fun getTargetDataKey() = KEY + + override fun postProcess( + toImport: MutableCollection>, + projectData: ProjectData?, + project: Project, + modelsProvider: IdeModifiableModelsProvider + ) { + super.postProcess(toImport, projectData, project, modelsProvider) + for (moduleModelNode in toImport) { + val moduleNode = ExternalSystemApiUtil.findParent(moduleModelNode, ProjectKeys.MODULE) ?: continue + val moduleData = moduleNode.data + val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue + val sourceSetName = moduleModelNode.data.selectedVariant.name + val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue + GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) } + } + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/gotoResourceHelper.kt.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/gotoResourceHelper.kt.as32 new file mode 100644 index 00000000000..3f92b2ebbb1 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/gotoResourceHelper.kt.as32 @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2015 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.android.navigation + +import org.jetbrains.android.util.AndroidResourceUtil +import com.intellij.psi.PsiElement +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.PsiClass +import org.jetbrains.android.util.AndroidUtils +import com.android.SdkConstants +import org.jetbrains.android.augment.AndroidPsiElementFinder +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression +import org.jetbrains.kotlin.psi.KtExpression + +fun getReferenceExpression(element: PsiElement?): KtSimpleNameExpression? { + return PsiTreeUtil.getParentOfType(element, KtSimpleNameExpression::class.java) +} + +// given 'R.a.b' returns info for all three parts of the expression 'a', 'b', 'R' +fun getInfo( + referenceExpression: KtSimpleNameExpression, + facet: AndroidFacet +): AndroidResourceUtil.MyReferredResourceFieldInfo? { + val info = getReferredInfo(referenceExpression, facet) + if (info != null) return info + + val topMostQualified = referenceExpression.getParentQualified().getParentQualified() ?: return null + val selectorCandidate = topMostQualified.selectorExpression as? KtSimpleNameExpression ?: return null + return getReferredInfo(selectorCandidate, facet) +} + +private fun KtExpression?.getParentQualified(): KtDotQualifiedExpression? { + return this?.parent as? KtDotQualifiedExpression +} + +// returns info if passed expression is 'b' in 'R.a.b' +private fun getReferredInfo( + lastPart: KtSimpleNameExpression, + facet: AndroidFacet +): AndroidResourceUtil.MyReferredResourceFieldInfo? { + val resFieldName = lastPart.getReferencedName() + if (resFieldName.isEmpty()) return null + + val middlePart = getReceiverAsSimpleNameExpression(lastPart) ?: return null + + val resClassName = middlePart.getReferencedName() + if (resClassName.isEmpty()) return null + + val firstPart = getReceiverAsSimpleNameExpression(middlePart) ?: return null + + val resolvedClass = firstPart.mainReference.resolve() as? PsiClass ?: return null + + //the following code is copied from + // org.jetbrains.android.util.AndroidResourceUtil.getReferredResourceOrManifestField + // (org.jetbrains.android.facet.AndroidFacet, com.intellij.psi.PsiReferenceExpression, java.lang.String, boolean) + val classShortName = resolvedClass.name + + val fromManifest = AndroidUtils.MANIFEST_CLASS_NAME == classShortName + + if (!fromManifest && AndroidUtils.R_CLASS_NAME != classShortName) { + return null + } + val qName = resolvedClass.qualifiedName + + if (SdkConstants.CLASS_R == qName || AndroidPsiElementFinder.INTERNAL_R_CLASS_QNAME == qName) { + return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, facet.module, true, false) + } + val containingFile = resolvedClass.containingFile ?: return null + + val isFromCorrectFile = + if (fromManifest) AndroidResourceUtil.isManifestJavaFile(facet, containingFile) + else AndroidResourceUtil.isRJavaFile(facet, containingFile) + + if (!isFromCorrectFile) { + return null + } + + return AndroidResourceUtil.MyReferredResourceFieldInfo(resClassName, resFieldName, facet.module, false, fromManifest) +} + +private fun getReceiverAsSimpleNameExpression(exp: KtSimpleNameExpression): KtSimpleNameExpression? { + val receiver = exp.getReceiverExpression() + return when (receiver) { + is KtSimpleNameExpression -> { + receiver + } + is KtDotQualifiedExpression -> { + receiver.selectorExpression as? KtSimpleNameExpression + } + else -> null + } + +} diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetVersionCheckQuickFix.kt.as32 b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetVersionCheckQuickFix.kt.as32 new file mode 100644 index 00000000000..fbc5b6d4289 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetVersionCheckQuickFix.kt.as32 @@ -0,0 +1,94 @@ +/* + * 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.android.quickfix + +import com.intellij.codeInsight.FileModificationService +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.android.inspections.lint.AndroidLintQuickFix +import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.codeInsight.surroundWith.statement.KotlinIfSurrounder +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.inspections.findExistingEditor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + + +class AddTargetVersionCheckQuickFix(val api: Int) : AndroidLintQuickFix { + + override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) { + val targetExpression = getTargetExpression(startElement) + val project = targetExpression?.project ?: return + val editor = targetExpression.findExistingEditor() ?: return + + val file = targetExpression.containingFile + val documentManager = PsiDocumentManager.getInstance(project) + val document = documentManager.getDocument(file) ?: return + + if (!FileModificationService.getInstance().prepareFileForWrite(file)) { + return + } + + val surrounder = getSurrounder(targetExpression, "\"VERSION.SDK_INT < ${getVersionField(api, false)}\"") + val conditionRange = surrounder.surroundElements(project, editor, arrayOf(targetExpression)) ?: return + val conditionText = "android.os.Build.VERSION.SDK_INT >= ${getVersionField(api, true)}" + document.replaceString(conditionRange.startOffset, conditionRange.endOffset, conditionText) + documentManager.commitDocument(document) + + ShortenReferences.DEFAULT.process(documentManager.getPsiFile(document) as KtFile, + conditionRange.startOffset, + conditionRange.startOffset + conditionText.length) + } + + override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean = + getTargetExpression(startElement) != null + + override fun getName(): String = "Surround with if (VERSION.SDK_INT >= VERSION_CODES.${getVersionField(api, false)}) { ... }" + + private fun getTargetExpression(element: PsiElement): KtElement? { + var current = PsiTreeUtil.getParentOfType(element, KtExpression::class.java) + while (current != null) { + if (current.parent is KtBlockExpression || + current.parent is KtContainerNode || + current.parent is KtWhenEntry || + current.parent is KtFunction || + current.parent is KtPropertyAccessor || + current.parent is KtProperty || + current.parent is KtReturnExpression || + current.parent is KtDestructuringDeclaration) { + break + } + current = PsiTreeUtil.getParentOfType(current, KtExpression::class.java, true) + } + + return current + } + + private fun getSurrounder(element: KtElement, todoText: String?): KotlinIfSurrounder { + val used = element.analyze(BodyResolveMode.PARTIAL_WITH_CFA)[BindingContext.USED_AS_EXPRESSION, element] ?: false + return if (used) { + object : KotlinIfSurrounder() { + override fun getCodeTemplate(): String = "if (a) { \n} else {\nTODO(${todoText ?: ""})\n}" + } + } else { + KotlinIfSurrounder() + } + } +} \ No newline at end of file diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt.as32 b/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/KotlinLintTestGenerated.java.as32 b/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/KotlinLintTestGenerated.java.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-gradle/build.gradle.kts.as32 b/idea/idea-gradle/build.gradle.kts.as32 new file mode 100644 index 00000000000..6ed9e321fad --- /dev/null +++ b/idea/idea-gradle/build.gradle.kts.as32 @@ -0,0 +1,67 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testRuntime(intellijDep()) + + compileOnly(project(":idea")) + compileOnly(project(":idea:idea-jvm")) + compile(project(":idea:kotlin-gradle-tooling")) + + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":compiler:frontend.script")) + + compile(project(":js:js.frontend")) + + compileOnly(intellijDep()) + compileOnly(intellijPluginDep("gradle")) + compileOnly(intellijPluginDep("Groovy")) + compileOnly(intellijPluginDep("junit")) + + testCompile(projectTests(":idea")) + testCompile(projectTests(":idea:idea-test-framework")) + + testCompile(intellijPluginDep("gradle")) + testCompileOnly(intellijPluginDep("Groovy")) + testCompileOnly(intellijDep()) + + testRuntime(projectDist(":kotlin-reflect")) + testRuntime(project(":idea:idea-jvm")) + testRuntime(project(":idea:idea-android")) + testRuntime(project(":plugins:kapt3-idea")) + testRuntime(project(":plugins:android-extensions-ide")) + testRuntime(project(":plugins:lint")) + testRuntime(project(":sam-with-receiver-ide-plugin")) + testRuntime(project(":allopen-ide-plugin")) + testRuntime(project(":noarg-ide-plugin")) + // TODO: the order of the plugins matters here, consider avoiding order-dependency + testRuntime(intellijPluginDep("junit")) + testRuntime(intellijPluginDep("testng")) + testRuntime(intellijPluginDep("properties")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("coverage")) + //testRuntime(intellijPluginDep("maven")) + testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) +} + +sourceSets { + "main" { + projectDefault() + resources.srcDir("res").apply { include("**") } + } + "test" { projectDefault() } +} + +testsJar() + +projectTest { + workingDir = rootDir + useAndroidSdk() +} + +configureInstrumentation() diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as32 b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as32 new file mode 100644 index 00000000000..032a789dea8 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleSourceSetDataService.kt.as32 @@ -0,0 +1,277 @@ +/* + * 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.configuration + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.LibraryData +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider +import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.isQualifiedModuleNamesEnabled +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.impl.libraries.LibraryEx +import com.intellij.openapi.roots.impl.libraries.LibraryImpl +import com.intellij.openapi.roots.libraries.PersistentLibraryKind +import com.intellij.openapi.util.Key +import com.intellij.util.PathUtil +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments +import org.jetbrains.kotlin.config.CoroutineSupport +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.gradle.ArgsInfo +import org.jetbrains.kotlin.gradle.CompilerArgumentsBySourceSet +import org.jetbrains.kotlin.idea.facet.* +import org.jetbrains.kotlin.idea.framework.CommonLibraryKind +import org.jetbrains.kotlin.idea.framework.JSLibraryKind +import org.jetbrains.kotlin.idea.framework.detectLibraryKind +import org.jetbrains.kotlin.idea.inspections.gradle.findAll +import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion +import org.jetbrains.kotlin.idea.inspections.gradle.getResolvedKotlinStdlibVersionByModuleData +import org.jetbrains.kotlin.psi.UserDataProperty +import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import java.io.File +import java.util.* + +var Module.compilerArgumentsBySourceSet + by UserDataProperty(Key.create("CURRENT_COMPILER_ARGUMENTS")) + +var Module.sourceSetName + by UserDataProperty(Key.create("SOURCE_SET_NAME")) + +interface GradleProjectImportHandler { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.gradleProjectImportHandler", + GradleProjectImportHandler::class.java + ) + + fun importBySourceSet(facet: KotlinFacet, sourceSetNode: DataNode) + fun importByModule(facet: KotlinFacet, moduleNode: DataNode) +} + +class KotlinGradleSourceSetDataService : AbstractProjectDataService() { + override fun getTargetDataKey() = GradleSourceSetData.KEY + + override fun postProcess( + toImport: Collection>, + projectData: ProjectData?, + project: Project, + modelsProvider: IdeModifiableModelsProvider + ) { + for (sourceSetNode in toImport) { + val sourceSetData = sourceSetNode.data + val ideModule = modelsProvider.findIdeModule(sourceSetData) ?: continue + + val moduleNode = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: continue + val sourceSetName = sourceSetNode.data.id.let { it.substring(it.lastIndexOf(':') + 1) } + val kotlinFacet = configureFacetByGradleModule(moduleNode, sourceSetName, ideModule, modelsProvider) ?: continue + GradleProjectImportHandler.getInstances(project).forEach { it.importBySourceSet(kotlinFacet, sourceSetNode) } + } + } +} + +class KotlinGradleProjectDataService : AbstractProjectDataService() { + override fun getTargetDataKey() = ProjectKeys.MODULE + + override fun postProcess( + toImport: MutableCollection>, + projectData: ProjectData?, + project: Project, + modelsProvider: IdeModifiableModelsProvider + ) { + for (moduleNode in toImport) { + // If source sets are present, configure facets in the their modules + if (ExternalSystemApiUtil.getChildren(moduleNode, GradleSourceSetData.KEY).isNotEmpty()) continue + + val moduleData = moduleNode.data + val ideModule = modelsProvider.findIdeModule(moduleData) ?: continue + val kotlinFacet = configureFacetByGradleModule(moduleNode, null, ideModule, modelsProvider) ?: continue + GradleProjectImportHandler.getInstances(project).forEach { it.importByModule(kotlinFacet, moduleNode) } + } + } +} + +class KotlinGradleLibraryDataService : AbstractProjectDataService() { + override fun getTargetDataKey() = ProjectKeys.LIBRARY + + override fun postProcess( + toImport: MutableCollection>, + projectData: ProjectData?, + project: Project, + modelsProvider: IdeModifiableModelsProvider + ) { + if (toImport.isEmpty()) return + val projectDataNode = toImport.first().parent!! + @Suppress("UNCHECKED_CAST") + val moduleDataNodes = projectDataNode.children.filter { it.data is ModuleData } as List> + val anyNonJvmModules = moduleDataNodes.any { detectPlatformByPlugin(it)?.takeIf { it !is TargetPlatformKind.Jvm } != null } + for (libraryDataNode in toImport) { + val ideLibrary = modelsProvider.findIdeLibrary(libraryDataNode.data) ?: continue + + val modifiableModel = modelsProvider.getModifiableLibraryModel(ideLibrary) as LibraryEx.ModifiableModelEx + if (anyNonJvmModules || ideLibrary.name?.looksAsNonJvmLibraryName() == true) { + detectLibraryKind(modifiableModel.getFiles(OrderRootType.CLASSES))?.let { modifiableModel.kind = it } + } else if (ideLibrary is LibraryImpl && (ideLibrary.kind === JSLibraryKind || ideLibrary.kind === CommonLibraryKind)) { + resetLibraryKind(modifiableModel) + } + } + } + + private fun String.looksAsNonJvmLibraryName() = nonJvmSuffixes.any { it in this } + + private fun resetLibraryKind(modifiableModel: LibraryEx.ModifiableModelEx) { + try { + val cls = LibraryImpl::class.java + // Don't use name-based lookup because field names are scrambled in IDEA Ultimate + for (field in cls.declaredFields) { + if (field.type == PersistentLibraryKind::class.java) { + field.isAccessible = true + field.set(modifiableModel, null) + return + } + } + LOG.info("Could not find field of type PersistentLibraryKind in LibraryImpl.class") + } catch (e: Exception) { + LOG.info("Failed to reset library kind", e) + } + } + + companion object { + val LOG = Logger.getInstance(KotlinGradleLibraryDataService::class.java) + + val nonJvmSuffixes = listOf("-common", "-js", "-native", "-kjsm") + } +} + +fun detectPlatformByPlugin(moduleNode: DataNode): TargetPlatformKind<*>? { + return when (moduleNode.platformPluginId) { + "kotlin-platform-jvm" -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6] + "kotlin-platform-js" -> TargetPlatformKind.JavaScript + "kotlin-platform-common" -> TargetPlatformKind.Common + else -> null + } +} + +private fun detectPlatformByLibrary(moduleNode: DataNode): TargetPlatformKind<*>? { + val detectedPlatforms = + mavenLibraryIdToPlatform.entries + .filter { moduleNode.getResolvedKotlinStdlibVersionByModuleData(listOf(it.key)) != null } + .map { it.value }.distinct() + return detectedPlatforms.singleOrNull() ?: detectedPlatforms.firstOrNull { it != TargetPlatformKind.Common } +} + +fun configureFacetByGradleModule( + moduleNode: DataNode, + sourceSetName: String?, + ideModule: Module, + modelsProvider: IdeModifiableModelsProvider +): KotlinFacet? { + if (!moduleNode.isResolved) return null + + if (!moduleNode.hasKotlinPlugin) { + val facetModel = modelsProvider.getModifiableFacetModel(ideModule) + val facet = facetModel.getFacetByType(KotlinFacetType.TYPE_ID) + if (facet != null) { + facetModel.removeFacet(facet) + } + return null + } + + val compilerVersion = moduleNode.findAll(BuildScriptClasspathData.KEY).firstOrNull()?.data?.let(::findKotlinPluginVersion) + ?: return null + val platformKind = detectPlatformByPlugin(moduleNode) ?: detectPlatformByLibrary(moduleNode) + + val coroutinesProperty = CoroutineSupport.byCompilerArgument( + moduleNode.coroutines ?: findKotlinCoroutinesProperty(ideModule.project) + ) + + val kotlinFacet = ideModule.getOrCreateFacet(modelsProvider, false) + kotlinFacet.configureFacet(compilerVersion, coroutinesProperty, platformKind, modelsProvider) + + ideModule.compilerArgumentsBySourceSet = moduleNode.compilerArgumentsBySourceSet + ideModule.sourceSetName = sourceSetName + + val argsInfo = moduleNode.compilerArgumentsBySourceSet?.get(sourceSetName ?: "main") + if (argsInfo != null) { + configureFacetByCompilerArguments(kotlinFacet, argsInfo, modelsProvider) + } + + with(kotlinFacet.configuration.settings) { + implementedModuleNames = getImplementedModuleNames(moduleNode, sourceSetName, ideModule.project) + productionOutputPath = getExplicitOutputPath(moduleNode, platformKind, "main") + testOutputPath = getExplicitOutputPath(moduleNode, platformKind, "test") + } + + kotlinFacet.noVersionAutoAdvance() + + return kotlinFacet +} + +fun configureFacetByCompilerArguments(kotlinFacet: KotlinFacet, argsInfo: ArgsInfo, modelsProvider: IdeModifiableModelsProvider?) { + val currentCompilerArguments = argsInfo.currentArguments + val defaultCompilerArguments = argsInfo.defaultArguments + val dependencyClasspath = argsInfo.dependencyClasspath.map { PathUtil.toSystemIndependentName(it) } + if (currentCompilerArguments.isNotEmpty()) { + parseCompilerArgumentsToFacet(currentCompilerArguments, defaultCompilerArguments, kotlinFacet, modelsProvider) + } + adjustClasspath(kotlinFacet, dependencyClasspath) +} + +private fun getImplementedModuleNames(moduleNode: DataNode, sourceSetName: String?, project: Project): List { + val baseModuleNames = moduleNode.implementedModuleNames + if (baseModuleNames.isEmpty() || sourceSetName == null) return baseModuleNames + val delimiter = if(isQualifiedModuleNamesEnabled(project)) "." else "_" + return baseModuleNames.map { "$it$delimiter$sourceSetName" } +} + +private fun getExplicitOutputPath(moduleNode: DataNode, platformKind: TargetPlatformKind<*>?, sourceSet: String): String? { + if (platformKind !== TargetPlatformKind.JavaScript) return null + val k2jsArgumentList = moduleNode.compilerArgumentsBySourceSet?.get(sourceSet)?.currentArguments ?: return null + return K2JSCompilerArguments().apply { parseCommandLineArguments(k2jsArgumentList, this) }.outputFile +} + +private fun adjustClasspath(kotlinFacet: KotlinFacet, dependencyClasspath: List) { + if (dependencyClasspath.isEmpty()) return + val arguments = kotlinFacet.configuration.settings.compilerArguments as? K2JVMCompilerArguments ?: return + val fullClasspath = arguments.classpath?.split(File.pathSeparator) ?: emptyList() + if (fullClasspath.isEmpty()) return + val newClasspath = fullClasspath - dependencyClasspath + arguments.classpath = if (newClasspath.isNotEmpty()) newClasspath.joinToString(File.pathSeparator) else null +} + +private val gradlePropertyFiles = listOf("local.properties", "gradle.properties") + +private fun findKotlinCoroutinesProperty(project: Project): String { + for (propertyFileName in gradlePropertyFiles) { + val propertyFile = project.baseDir.findChild(propertyFileName) ?: continue + val properties = Properties() + properties.load(propertyFile.inputStream) + properties.getProperty("kotlin.coroutines")?.let { return it } + } + + return CoroutineSupport.getCompilerArgument(LanguageFeature.Coroutines.defaultState) +} diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as32 b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as32 new file mode 100644 index 00000000000..5af0cbe204c --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt.as32 @@ -0,0 +1,375 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.configuration + +import com.intellij.codeInsight.CodeInsightUtilCore +import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix +import com.intellij.ide.actions.OpenFileAction +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.openapi.fileEditor.OpenFileDescriptor +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleUtil +import com.intellij.openapi.project.Project +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.roots.DependencyScope +import com.intellij.openapi.roots.ExternalLibraryDescriptor +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.WritingAccessProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.config.CoroutineSupport +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion +import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion +import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix +import org.jetbrains.kotlin.idea.util.application.executeCommand +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor +import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.plugins.gradle.util.GradleConstants +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile +import java.io.File +import java.util.* + +abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { + + override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus { + val module = moduleSourceRootGroup.baseModule + if (!isApplicable(module)) { + return ConfigureKotlinStatus.NON_APPLICABLE + } + + if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) { + return ConfigureKotlinStatus.CONFIGURED + } + + val buildFiles = runReadAction { + listOf( + module.getBuildScriptPsiFile(), + module.project.getTopLevelBuildScriptPsiFile() + ).filterNotNull() + } + + if (buildFiles.isEmpty()) { + return ConfigureKotlinStatus.NON_APPLICABLE + } + + if (buildFiles.none { it.isConfiguredByAnyGradleConfigurator() }) { + return ConfigureKotlinStatus.CAN_BE_CONFIGURED + } + + return ConfigureKotlinStatus.BROKEN + } + + private fun PsiFile.isConfiguredByAnyGradleConfigurator(): Boolean { + return Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME) + .filterIsInstance() + .any { it.isFileConfigured(this) } + } + + protected open fun isApplicable(module: Module): Boolean = + module.getBuildSystemType() == Gradle + + protected open fun getMinimumSupportedVersion() = "1.0.0" + + private fun isFileConfigured(buildScript: PsiFile): Boolean = getManipulator(buildScript).isConfigured(kotlinPluginName) + + @JvmSuppressWildcards + override fun configure(project: Project, excludeModules: Collection) { + val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion()) + + dialog.show() + if (!dialog.isOK) return + + val collector = configureSilently(project, dialog.modulesToConfigure, dialog.kotlinVersion) + collector.showNotification() + } + + fun configureSilently(project: Project, modules: List, version: String): NotificationMessageCollector { + return project.executeCommand("Configure Kotlin") { + val collector = createConfigureKotlinNotificationCollector(project) + val changedFiles = configureWithVersion(project, modules, version, collector) + + for (file in changedFiles) { + OpenFileAction.openFile(file.virtualFile, project) + } + collector + } + } + + fun configureWithVersion( + project: Project, + modulesToConfigure: List, + kotlinVersion: String, + collector: NotificationMessageCollector + ): HashSet { + val filesToOpen = HashSet() + val buildScript = project.getTopLevelBuildScriptPsiFile() + if (buildScript != null && canConfigureFile(buildScript)) { + val isModified = configureBuildScript(buildScript, true, kotlinVersion, collector) + if (isModified) { + filesToOpen.add(buildScript) + } + } + + for (module in modulesToConfigure) { + val file = module.getBuildScriptPsiFile() + if (file != null && canConfigureFile(file)) { + configureModule(module, file, false, kotlinVersion, collector, filesToOpen) + } else { + showErrorMessage(project, "Cannot find build.gradle file for module " + module.name) + } + } + return filesToOpen + } + + open fun configureModule( + module: Module, + file: PsiFile, + isTopLevelProjectFile: Boolean, + version: String, + collector: NotificationMessageCollector, + filesToOpen: MutableCollection + ) { + val isModified = configureBuildScript(file, isTopLevelProjectFile, version, collector) + if (isModified) { + filesToOpen.add(file) + } + } + + protected fun configureModuleBuildScript(file: PsiFile, version: String): Boolean { + val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk } + val jvmTarget = getJvmTarget(sdk, version) + return getManipulator(file).configureModuleBuildScript( + kotlinPluginName, + getStdlibArtifactName(sdk, version), + version, + jvmTarget + ) + } + + protected open fun getStdlibArtifactName(sdk: Sdk?, version: String) = getStdlibArtifactId(sdk, version) + + protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null + + protected abstract val kotlinPluginName: String + + protected open fun addElementsToFile( + file: PsiFile, + isTopLevelProjectFile: Boolean, + version: String + ): Boolean { + if (!isTopLevelProjectFile) { + var wasModified = configureProjectFile(file, version) + wasModified = wasModified or configureModuleBuildScript(file, version) + return wasModified + } + return false + } + + private fun configureBuildScript( + file: PsiFile, + isTopLevelProjectFile: Boolean, + version: String, + collector: NotificationMessageCollector + ): Boolean { + val isModified = file.project.executeWriteCommand("Configure ${file.name}", null) { + val isModified = addElementsToFile(file, isTopLevelProjectFile, version) + + CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file) + isModified + } + + val virtualFile = file.virtualFile + if (virtualFile != null && isModified) { + collector.addMessage(virtualFile.path + " was modified") + } + return isModified + } + + override fun updateLanguageVersion( + module: Module, + languageVersion: String?, + apiVersion: String?, + requiredStdlibVersion: ApiVersion, + forTests: Boolean + ) { + val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion -> + runtimeVersion < requiredStdlibVersion + } ?: false + + if (runtimeUpdateRequired) { + Messages.showErrorDialog( + module.project, + "This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " + + "Please update the version in your build script.", + "Update Language Version" + ) + return + } + + val element = changeLanguageVersion(module, languageVersion, apiVersion, forTests) + + element?.let { + OpenFileDescriptor(module.project, it.containingFile.virtualFile, it.textRange.startOffset).navigate(true) + } + } + + override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) { + val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED && + (getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false) + + if (runtimeUpdateRequired) { + Messages.showErrorDialog( + module.project, + "Coroutines support requires version 1.1 or later of the Kotlin runtime library. " + + "Please update the version in your build script.", + ChangeCoroutineSupportFix.getFixText(state) + ) + return + } + + val element = changeCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state)) + if (element != null) { + OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true) + } + } + + override fun addLibraryDependency( + module: Module, + element: PsiElement, + library: ExternalLibraryDescriptor, + libraryJarDescriptors: List + ) { + val scope = OrderEntryFix.suggestScopeByLocation(module, element) + KotlinWithGradleConfigurator.addKotlinLibraryToModule(module, scope, library) + } + + companion object { + fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) { + is KtFile -> KotlinBuildScriptManipulator(file) + is GroovyFile -> GroovyBuildScriptManipulator(file) + else -> error("Unknown build script file type!") + } + + val GROUP_ID = "org.jetbrains.kotlin" + val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin" + + val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\"" + + private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts" + + fun getGroovyDependencySnippet(artifactName: String, scope: String) = + "$scope \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\"" + + fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'" + + fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) { + val buildScript = module.getBuildScriptPsiFile() ?: return + if (!canConfigureFile(buildScript)) { + return + } + + getManipulator(buildScript) + .addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor, module.getBuildSystemType() == AndroidGradle) + + buildScript.virtualFile?.let { + createConfigureKotlinNotificationCollector(buildScript.project) + .addMessage(it.path + " was modified") + .showNotification() + } + } + + fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? = changeBuildGradle(module) { + getManipulator(it).changeCoroutineConfiguration(coroutineOption) + } + + fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) = + changeBuildGradle(module) { buildScriptFile -> + val manipulator = getManipulator(buildScriptFile) + var result: PsiElement? = null + if (languageVersion != null) { + result = manipulator.changeLanguageVersion(languageVersion, forTests) + } + + if (apiVersion != null) { + result = manipulator.changeApiVersion(apiVersion, forTests) + } + + result + } + + private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? { + val buildScriptFile = module.getBuildScriptPsiFile() + if (buildScriptFile != null && canConfigureFile(buildScriptFile)) { + return buildScriptFile.project.executeWriteCommand("Change build.gradle configuration", null) { + body(buildScriptFile) + } + } + return null + } + + fun getKotlinStdlibVersion(module: Module): String? { + return module.getBuildScriptPsiFile()?.let { + getManipulator(it).getKotlinStdlibVersion() + } + } + + fun configureProjectFile(file: PsiFile, version: String): Boolean = getManipulator(file).configureProjectBuildScript(version) + + private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null) + + private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project) + + private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { findBuildGradleFile(it)?.getPsiFile(this) } + + private fun Module.getBuildScriptFile(): File? { + val moduleDir = File(moduleFilePath).parent + findBuildGradleFile(moduleDir)?.let { + return it + } + + ModuleRootManager.getInstance(this).contentRoots.forEach { root -> + findBuildGradleFile(root.path)?.let { + return it + } + } + + ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath -> + findBuildGradleFile(externalProjectPath)?.let { + return it + } + } + + return null + } + + private fun findBuildGradleFile(path: String): File? = + File(path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME).takeIf { it.exists() } + ?: File(path + "/" + KOTLIN_BUILD_SCRIPT_NAME).takeIf { it.exists() } + + private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let { + PsiManager.getInstance(project).findFile(it) + } + + private fun showErrorMessage(project: Project, message: String?) { + Messages.showErrorDialog( + project, + "Couldn't configure kotlin-gradle plugin automatically.
" + + (if (message != null) message + "
" else "") + + "
See manual installation instructions here.", + "Configure Kotlin-Gradle Plugin" + ) + } + } +} diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt.as32 b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt.as32 new file mode 100644 index 00000000000..3b940b76266 --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleFacetImportTest.kt.as32 @@ -0,0 +1,1912 @@ +/* + * 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.codeInsight.gradle + +import com.intellij.openapi.application.Result +import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.projectRoots.JavaSdk +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.openapi.roots.LibraryOrderEntry +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.impl.libraries.LibraryEx +import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinStatus +import org.jetbrains.kotlin.idea.configuration.ModuleSourceRootMap +import org.jetbrains.kotlin.idea.configuration.allConfigurators +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.kotlin.idea.framework.CommonLibraryKind +import org.jetbrains.kotlin.idea.framework.JSLibraryKind +import org.jetbrains.kotlin.idea.framework.KotlinSdkType +import org.jetbrains.kotlin.idea.util.projectStructure.allModules +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.junit.Assert +import org.junit.Test + +internal fun GradleImportingTestCase.facetSettings(moduleName: String) = KotlinFacet.get(getModule(moduleName))!!.configuration.settings + +internal val GradleImportingTestCase.facetSettings: KotlinFacetSettings + get() = facetSettings("project") + +internal val GradleImportingTestCase.testFacetSettings: KotlinFacetSettings + get() = facetSettings("project_test") + +class GradleFacetImportTest : GradleImportingTestCase() { + @Test + fun testJvmImport() { + createProjectSubFile( + "build.gradle", """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + + compileKotlin { + kotlinOptions.jvmTarget = "1.7" + kotlinOptions.freeCompilerArgs = ["-Xsingle-module", "-Xdump-declarations-to", "tmp"] + } + + compileTestKotlin { + kotlinOptions.jvmTarget = "1.6" + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-Xdump-declarations-to", "tmpTest"] + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertFalse(compilerArguments!!.autoAdvanceLanguageVersion) + Assert.assertFalse(compilerArguments!!.autoAdvanceApiVersion) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], targetPlatformKind) + Assert.assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmp -Xsingle-module", + compilerSettings!!.additionalArguments + ) + } +/* + with (testFacetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertFalse(compilerArguments!!.autoAdvanceLanguageVersion) + Assert.assertFalse(compilerArguments!!.autoAdvanceApiVersion) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + Assert.assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmpTest", + compilerSettings!!.additionalArguments + ) + } +*/ + assertAllModulesConfigured() + } + + @Test + fun testJvmImportWithPlugin() { + createProjectSubFile( + "build.gradle", """ +buildscript { + repositories { + mavenCentral() + } +} + +plugins { + id "org.jetbrains.kotlin.jvm" version "1.1.3" +} + +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:1.1.3" + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} + """ + ) + importProject() + + assertAllModulesConfigured() + } + + @Test + fun testJvmImport_1_1_2() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-dev' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-5") + } + } + + apply plugin: 'kotlin' + + repositories { + mavenCentral() + maven { url 'http://dl.bintray.com/kotlin/kotlin-dev' } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.2-5" + } + + compileKotlin { + kotlinOptions.jvmTarget = "1.7" + kotlinOptions.freeCompilerArgs = ["-Xsingle-module", "-Xdump-declarations-to", "tmp"] + } + + compileTestKotlin { + kotlinOptions.jvmTarget = "1.6" + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-Xdump-declarations-to", "tmpTest"] + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], targetPlatformKind) + Assert.assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmp -Xsingle-module", + compilerSettings!!.additionalArguments + ) + } +/* + with (testFacetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + Assert.assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmpTest", + compilerSettings!!.additionalArguments + ) + } +*/ + } + + @Test + fun testJvmImportWithCustomSourceSets() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + + compileMyMainKotlin { + kotlinOptions.jvmTarget = "1.7" + kotlinOptions.freeCompilerArgs = ["-Xsingle-module", "-Xdump-declarations-to", "tmp"] + } + + compileMyTestKotlin { + kotlinOptions.jvmTarget = "1.6" + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-Xdump-declarations-to", "tmpTest"] + } + """ + ) + importProject() + +/* + with (facetSettings("project_myMain")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], targetPlatformKind) + Assert.assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmp -Xsingle-module", + compilerSettings!!.additionalArguments + ) + } + with(facetSettings("project_myTest")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + Assert.assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmpTest", + compilerSettings!!.additionalArguments + ) + } +*/ + assertAllModulesConfigured() + } + + @Test + fun testJvmImportWithCustomSourceSets_1_1_2() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { url 'http://dl.bintray.com/kotlin/kotlin-dev' } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-5") + } + } + + apply plugin: 'kotlin' + + repositories { + mavenCentral() + maven { url 'http://dl.bintray.com/kotlin/kotlin-dev' } + } + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.2-5" + } + + compileMyMainKotlin { + kotlinOptions.jvmTarget = "1.7" + kotlinOptions.freeCompilerArgs = ["-Xsingle-module", "-Xdump-declarations-to", "tmp"] + } + + compileMyTestKotlin { + kotlinOptions.jvmTarget = "1.6" + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-Xdump-declarations-to", "tmpTest"] + } + """ + ) + importProject() +/* + with (facetSettings("project_myMain")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_8], targetPlatformKind) + Assert.assertEquals("1.7", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmp -Xsingle-module", + compilerSettings!!.additionalArguments + ) + } + with(facetSettings("project_myTest")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + Assert.assertEquals("1.6", (compilerArguments as K2JVMCompilerArguments).jvmTarget) + Assert.assertEquals( + "-Xdump-declarations-to=tmpTest", + compilerSettings!!.additionalArguments + ) + } +*/ + } + + @Test + fun testCoroutineImportByOptions() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + + kotlin { + experimental { + coroutines 'enable' + } + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals(LanguageFeature.State.ENABLED, coroutineSupport) + } + } + + @Test + fun testCoroutineImportByProperties() { + createProjectSubFile("gradle.properties", "kotlin.coroutines=enable") + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals(LanguageFeature.State.ENABLED, coroutineSupport) + } + } + + @Test + fun testJsImport() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin2js' + + repositories { + mavenCentral() + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + } + + compileKotlin2Js { + kotlinOptions.sourceMap = true + kotlinOptions.freeCompilerArgs = ["-module-kind", "plain", "-main", "callMain"] + } + + compileTestKotlin2Js { + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-module-kind", "umd", "-main", "callTest"] + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertFalse(compilerArguments!!.autoAdvanceLanguageVersion) + Assert.assertFalse(compilerArguments!!.autoAdvanceApiVersion) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(true, sourceMap) + Assert.assertEquals("plain", moduleKind) + } + Assert.assertEquals( + "-main callMain", + compilerSettings!!.additionalArguments + ) + } +/* + with (testFacetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertFalse(compilerArguments!!.autoAdvanceLanguageVersion) + Assert.assertFalse(compilerArguments!!.autoAdvanceApiVersion) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(false, sourceMap) + Assert.assertEquals("umd", moduleKind) + } + Assert.assertEquals( + "-main callTest", + compilerSettings!!.additionalArguments + ) + } +*/ + val rootManager = ModuleRootManager.getInstance(getModule("project")) + val stdlib = rootManager.orderEntries.filterIsInstance().first().library + assertEquals(JSLibraryKind, (stdlib as LibraryEx).kind) + assertTrue(stdlib.getFiles(OrderRootType.CLASSES).isNotEmpty()) + + Assert.assertTrue(ModuleRootManager.getInstance(getModule("project_main")).sdk!!.sdkType is KotlinSdkType) + Assert.assertTrue(ModuleRootManager.getInstance(getModule("project_test")).sdk!!.sdkType is KotlinSdkType) + + assertAllModulesConfigured() + } + + @Test + fun testJsImportTransitive() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin2js' + + repositories { + mavenCentral() + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-test-js:1.1.0" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + } + + val rootManager = ModuleRootManager.getInstance(getModule("project")) + val stdlib = rootManager.orderEntries + .filterIsInstance() + .map { it.library as LibraryEx } + .first { "kotlin-stdlib-js" in it.name!! } + assertEquals(JSLibraryKind, stdlib.kind) + + assertAllModulesConfigured() + } + + @Test + fun testJsImportWithCustomSourceSets() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin2js' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + } + + compileMyMainKotlin2Js { + kotlinOptions.sourceMap = true + kotlinOptions.freeCompilerArgs = ["-module-kind", "plain", "-main", "callMain"] + } + + compileMyTestKotlin2Js { + kotlinOptions.apiVersion = "1.0" + kotlinOptions.freeCompilerArgs = ["-module-kind", "umd", "-main", "callTest"] + } + """ + ) + importProject() +/* + with (facetSettings("project_myMain")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(true, sourceMap) + Assert.assertEquals("plain", moduleKind) + } + Assert.assertEquals( + "-main callMain", + compilerSettings!!.additionalArguments + ) + } + + with(facetSettings("project_myTest")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + with(compilerArguments as K2JSCompilerArguments) { + Assert.assertEquals(false, sourceMap) + Assert.assertEquals("umd", moduleKind) + } + Assert.assertEquals( + "-main callTest", + compilerSettings!!.additionalArguments + ) + } +*/ + assertAllModulesConfigured() + } + + @Test + fun testDetectOldJsStdlib() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.6") + } + } + + apply plugin: 'kotlin2js' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-js-library:1.0.6" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + } + } + + @Test + fun testJvmImportByPlatformPlugin() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-jvm' + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + } + } + + @Test + fun testJsImportByPlatformPlugin() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-js' + + repositories { + mavenCentral() + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + } + + val rootManager = ModuleRootManager.getInstance(getModule("project")) + val libraries = rootManager.orderEntries.filterIsInstance().mapNotNull { it.library as LibraryEx } + assertEquals(JSLibraryKind, libraries.first { it.name?.contains("kotlin-stdlib-js") == true }.kind) + assertEquals(CommonLibraryKind, libraries.first { it.name?.contains("kotlin-stdlib-common") == true }.kind) + } + + @Test + fun testCommonImportByPlatformPlugin() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-common' + + repositories { + mavenCentral() + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + } + + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Common, targetPlatformKind) + } + + val rootManager = ModuleRootManager.getInstance(getModule("project")) + val stdlib = rootManager.orderEntries.filterIsInstance().first().library + assertEquals(CommonLibraryKind, (stdlib as LibraryEx).kind) + } + + @Test + fun testCommonImportByPlatformPlugin_SingleModule() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + jcenter() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-common' + + repositories { + mavenCentral() + jcenter() + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + } + + """ + ) + importProjectUsingSingeModulePerGradleProject() + + with(facetSettings("project")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Common, targetPlatformKind) + } + + val rootManager = ModuleRootManager.getInstance(getModule("project")) + rootManager.orderEntries.filterIsInstance().mapTo(HashSet()) { it.library }.first { + (it as LibraryEx).kind == CommonLibraryKind + } + } + + @Test + fun testJvmImportByKotlinPlugin() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], targetPlatformKind) + } + } + + @Test + fun testJsImportByKotlin2JsPlugin() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin2js' + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + } + } + + @Test + fun testArgumentEscaping() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-jvm' + + compileKotlin { + kotlinOptions.freeCompilerArgs = ["-module", "module with spaces"] + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals( + listOf("-Xbuild-file=module with spaces"), + compilerSettings!!.additionalArgumentsAsList + ) + } + } + + @Test + fun testNoPluginsInAdditionalArgs() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.10") + classpath("org.jetbrains.kotlin:kotlin-allopen:1.2.10") + } + } + + apply plugin: 'kotlin' + apply plugin: "kotlin-spring" + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals( + "-version", + compilerSettings!!.additionalArguments + ) + Assert.assertEquals( + listOf( + "plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.stereotype.Component", + "plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.transaction.annotation.Transactional", + "plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.scheduling.annotation.Async", + "plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.cache.annotation.Cacheable", + "plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.boot.test.context.SpringBootTest", + "plugin:org.jetbrains.kotlin.allopen:annotation=org.springframework.validation.annotation.Validated" + ), + compilerArguments!!.pluginOptions!!.toList() + ) + } + } + + @Test + fun testNoArgInvokeInitializers() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.10") + classpath("org.jetbrains.kotlin:kotlin-noarg:1.2.10") + } + } + + apply plugin: 'kotlin' + apply plugin: "kotlin-noarg" + + noArg { + invokeInitializers = true + annotation("NoArg") + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals( + "-version", + compilerSettings!!.additionalArguments + ) + Assert.assertEquals( + listOf( + "plugin:org.jetbrains.kotlin.noarg:annotation=NoArg", + "plugin:org.jetbrains.kotlin.noarg:invokeInitializers=true" + ), + compilerArguments!!.pluginOptions!!.toList() + ) + } + } + + @Test + fun testAndroidGradleJsDetection() { + createProjectSubFile( + "android-module/build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:2.3.0" + } + } + + apply plugin: 'com.android.application' + + android { + compileSdkVersion 26 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 11 + targetSdkVersion 23 + versionCode 1002003 + versionName version + } + + dataBinding { + enabled = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + buildTypes { + debug { + applicationIdSuffix ".debug" + versionNameSuffix "-debug" + } + release { + minifyEnabled true + shrinkResources true + } + } + } + """ + ) + createProjectSubFile( + "android-module/src/main/AndroidManifest.xml", """ + + + """ + ) + createProjectSubFile( + "js-module/build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-dev' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-eap-44") + } + } + + apply plugin: 'kotlin2js' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + } + """ + ) + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenLocal() + maven { + url='https://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:2.3.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0" + } + } + + ext { + androidBuildToolsVersion = '23.0.1' + } + + allprojects { + repositories { + mavenLocal() + maven { + url='https://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + jcenter() + } + } + """ + ) + createProjectSubFile( + "settings.gradle", """ + rootProject.name = "android-js-test" + include ':android-module' + include ':js-module' + """ + ) + createProjectSubFile( + "local.properties", """ + sdk.dir=/${KotlinTestUtils.getAndroidSdkSystemIndependentPath()} + """ + ) + importProject() + + with(facetSettings("js-module")) { + Assert.assertEquals(TargetPlatformKind.JavaScript, targetPlatformKind) + } + + val rootManager = ModuleRootManager.getInstance(getModule("js-module")) + val stdlib = rootManager.orderEntries.filterIsInstance().single().library!! + assertTrue(stdlib.getFiles(OrderRootType.CLASSES).isNotEmpty()) + assertEquals(JSLibraryKind, (stdlib as LibraryEx).kind) + } + + @Test + fun testKotlinAndroidPluginDetection() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + jcenter() + maven { + url='https://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath "com.android.tools.build:gradle:2.3.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0" + } + } + + apply plugin: 'com.android.application' + apply plugin: 'kotlin-android' + + android { + compileSdkVersion 26 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 11 + targetSdkVersion 23 + versionCode 1002003 + versionName version + } + + dataBinding { + enabled = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + buildTypes { + debug { + applicationIdSuffix ".debug" + versionNameSuffix "-debug" + } + release { + minifyEnabled true + shrinkResources true + } + } + } + """ + ) + createProjectSubFile( + "local.properties", """ + sdk.dir=/${KotlinTestUtils.getAndroidSdkSystemIndependentPath()} + """ + ) + createProjectSubFile( + "src/main/AndroidManifest.xml", """ + + + """ + ) + importProject() + + Assert.assertNotNull(KotlinFacet.get(getModule("project"))) + } + + @Test + fun testNoFacetInModuleWithoutKotlinPlugin() { + createProjectSubFile( + "build.gradle", """ + group 'gr01' + version '1.0-SNAPSHOT' + + apply plugin: 'java' + apply plugin: 'kotlin' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.1" + } + } + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:1.1.1" + } + """ + ) + createProjectSubFile( + "settings.gradle", """ + rootProject.name = 'gr01' + include 'm1' + """ + ) + createProjectSubFile( + "m1/build.gradle", """ + group 'gr01' + version '1.0-SNAPSHOT' + + apply plugin: 'java' + + sourceCompatibility = 1.8 + + repositories { + mavenCentral() + } + + buildscript { + repositories { + mavenCentral() + } + } + dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' + } + """ + ) + importProject() + + Assert.assertNotNull(KotlinFacet.get(getModule("gr01"))) +// Assert.assertNotNull(KotlinFacet.get(getModule("gr01_test"))) + Assert.assertNull(KotlinFacet.get(getModule("m1"))) +// Assert.assertNull(KotlinFacet.get(getModule("m1_test"))) + } + + @Test + fun testClasspathWithDependenciesImport() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + compile "org.apache.logging.log4j:log4j-core:2.7" + } + + compileKotlin { + kotlinOptions.freeCompilerArgs += ["-cp", "tmp.jar"] + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("tmp.jar", (compilerArguments as K2JVMCompilerArguments).classpath) + } + } + + @Test + fun testDependenciesClasspathImport() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + compile "org.apache.logging.log4j:log4j-core:2.7" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals(null, (compilerArguments as K2JVMCompilerArguments).classpath) + } + } + + @Test + fun testJDKImport() { + object : WriteAction() { + override fun run(result: Result) { + val jdk = JavaSdk.getInstance().createJdk("myJDK", "my/path/to/jdk") + ProjectJdkTable.getInstance().addJdk(jdk) + } + }.execute() + + try { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + compile "org.apache.logging.log4j:log4j-core:2.7" + } + + compileKotlin { + kotlinOptions.jdkHome = "my/path/to/jdk" + } + """ + ) + importProject() + + val moduleSDK = ModuleRootManager.getInstance(getModule("project")).sdk!! + Assert.assertTrue(moduleSDK.sdkType is JavaSdk) + Assert.assertEquals("myJDK", moduleSDK.name) + Assert.assertEquals("my/path/to/jdk", moduleSDK.homePath) + } finally { + object : WriteAction() { + override fun run(result: Result) { + val jdkTable = ProjectJdkTable.getInstance() + jdkTable.removeJdk(jdkTable.findJdk("myJDK")!!) + } + }.execute() + } + } + + @Test + fun testImplementsDependency() { + createProjectSubFile( + "build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-common' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + } + + """.trimIndent() + ) + createProjectSubFile( + "settings.gradle", + """ + rootProject.name = 'MultiTest' + include 'MultiTest-jvm', 'MultiTest-js' + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-js/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-js' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-jvm/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-jvm' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + + importProject() +/* + Assert.assertEquals(listOf("MultiTest_main"), facetSettings("MultiTest-jvm_main").implementedModuleNames) + Assert.assertEquals(listOf("MultiTest_test"), facetSettings("MultiTest-jvm_test").implementedModuleNames) + Assert.assertEquals(listOf("MultiTest_main"), facetSettings("MultiTest-js_main").implementedModuleNames) + Assert.assertEquals(listOf("MultiTest_test"), facetSettings("MultiTest-js_test").implementedModuleNames) +*/ + } + + @Test + fun testImplementsDependencyWithCustomSourceSets() { + createProjectSubFile( + "build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-common' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.1.0" + } + + """.trimIndent() + ) + createProjectSubFile( + "settings.gradle", + """ + rootProject.name = 'MultiTest' + include 'MultiTest-jvm', 'MultiTest-js' + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-js/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-js' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + createProjectSubFile( + "MultiTest-jvm/build.gradle", + """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin-platform-jvm' + + sourceSets { + myMain { + kotlin { + srcDir 'src' + } + } + myTest { + kotlin { + srcDir 'test' + } + } + } + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + implement project(":") + } + + """.trimIndent() + ) + + importProject() +/* + Assert.assertEquals(listOf("MultiTest_myMain"), facetSettings("MultiTest-jvm_myMain").implementedModuleNames) + Assert.assertEquals(listOf("MultiTest_myTest"), facetSettings("MultiTest-jvm_myTest").implementedModuleNames) + Assert.assertEquals(listOf("MultiTest_myMain"), facetSettings("MultiTest-js_myMain").implementedModuleNames) + Assert.assertEquals(listOf("MultiTest_myTest"), facetSettings("MultiTest-js_myTest").implementedModuleNames) +*/ + } + + @Test + fun testAPIVersionExceedingLanguageVersion() { + createProjectSubFile( + "build.gradle", """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + + compileKotlin { + kotlinOptions.languageVersion = "1.1" + kotlinOptions.apiVersion = "1.2" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + } + + assertAllModulesConfigured() + } + + @Test + fun testIgnoreProjectLanguageAndAPIVersion() { + KotlinCommonCompilerArgumentsHolder.getInstance(myProject).update { + languageVersion = "1.0" + apiVersion = "1.0" + } + + createProjectSubFile( + "build.gradle", """ + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.0" + } + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.1", apiLevel!!.versionString) + } + + assertAllModulesConfigured() + } + + @Test + fun testCommonArgumentsImport() { + createProjectSubFile( + "build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' + } + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0-rc-39") + } + } + + apply plugin: 'kotlin-platform-common' + + repositories { + mavenCentral() + maven { + url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' + } + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.0-rc-39" + } + + compileKotlinCommon{ + kotlinOptions { + languageVersion = 1.1 + apiVersion = 1.0 + freeCompilerArgs += ["-cp", "my/classpath"] + freeCompilerArgs += ["-d", "my/destination"] + } + } + + compileTestKotlinCommon{ + kotlinOptions { + languageVersion = 1.1 + apiVersion = 1.0 + freeCompilerArgs += ["-cp", "my/test/classpath"] + freeCompilerArgs += ["-d", "my/test/destination"] + } + } + + """ + ) + importProject() + + with(facetSettings) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertFalse(compilerArguments!!.autoAdvanceLanguageVersion) + Assert.assertFalse(compilerArguments!!.autoAdvanceApiVersion) + Assert.assertEquals(TargetPlatformKind.Common, targetPlatformKind) + Assert.assertEquals("my/classpath", (compilerArguments as K2MetadataCompilerArguments).classpath) + Assert.assertEquals("my/destination", (compilerArguments as K2MetadataCompilerArguments).destination) + } + + with(facetSettings("project_test")) { + Assert.assertEquals("1.1", languageLevel!!.versionString) + Assert.assertEquals("1.0", apiLevel!!.versionString) + Assert.assertFalse(compilerArguments!!.autoAdvanceLanguageVersion) + Assert.assertFalse(compilerArguments!!.autoAdvanceApiVersion) + Assert.assertEquals(TargetPlatformKind.Common, targetPlatformKind) + Assert.assertEquals("my/test/classpath", (compilerArguments as K2MetadataCompilerArguments).classpath) + Assert.assertEquals("my/test/destination", (compilerArguments as K2MetadataCompilerArguments).destination) + } + + val rootManager = ModuleRootManager.getInstance(getModule("project_main")) + val stdlib = rootManager.orderEntries.filterIsInstance().single().library + assertEquals(CommonLibraryKind, (stdlib as LibraryEx).kind) + + Assert.assertTrue(ModuleRootManager.getInstance(getModule("project_main")).sdk!!.sdkType is KotlinSdkType) + Assert.assertTrue(ModuleRootManager.getInstance(getModule("project_test")).sdk!!.sdkType is KotlinSdkType) + } + + private fun assertAllModulesConfigured() { + runReadAction { + for (moduleGroup in ModuleSourceRootMap(myProject).groupByBaseModules(myProject.allModules())) { + val configurator = allConfigurators().find { + it.getStatus(moduleGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED + } + Assert.assertNull("Configurator $configurator tells that ${moduleGroup.baseModule} can be configured", configurator) + } + } + } +} diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/framework/JavaRuntimePresentationProvider.java.as32 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/framework/JavaRuntimePresentationProvider.java.as32 new file mode 100644 index 00000000000..dbd8b62a618 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/framework/JavaRuntimePresentationProvider.java.as32 @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2015 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.framework; + +import com.intellij.framework.library.LibraryVersionProperties; +import com.intellij.openapi.roots.libraries.LibraryPresentationProvider; +import com.intellij.openapi.roots.libraries.LibraryProperties; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.idea.KotlinIcons; + +import javax.swing.*; +import java.util.List; + +public class JavaRuntimePresentationProvider extends LibraryPresentationProvider { + public static JavaRuntimePresentationProvider getInstance() { + return LibraryPresentationProvider.EP_NAME.findExtension(JavaRuntimePresentationProvider.class); + } + + protected JavaRuntimePresentationProvider() { + super(JavaRuntimeLibraryDescription.Companion.getKOTLIN_JAVA_RUNTIME_KIND()); + } + + @Nullable + @Override + public Icon getIcon(@Nullable LibraryVersionProperties properties) { + return KotlinIcons.SMALL_LOGO; + } + + @Nullable + @Override + public LibraryVersionProperties detect(@NotNull List classesRoots) { + String version = JavaRuntimeDetectionUtil.getJavaRuntimeVersion(classesRoots); + return version == null ? null : new LibraryVersionProperties(version); + } +} diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt.as32 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt.as32 new file mode 100644 index 00000000000..01db58b93f2 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt.as32 @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ex.ProjectRootManagerEx +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.configuration.BuildSystemType +import org.jetbrains.kotlin.idea.configuration.findApplicableConfigurator +import org.jetbrains.kotlin.idea.configuration.getBuildSystemType +import org.jetbrains.kotlin.idea.facet.KotlinFacet + +import org.jetbrains.kotlin.psi.KtFile + +sealed class ChangeCoroutineSupportFix( + element: PsiElement, + protected val coroutineSupport: LanguageFeature.State +) : KotlinQuickFixAction(element) { + protected val coroutineSupportEnabled: Boolean + get() = coroutineSupport == LanguageFeature.State.ENABLED || coroutineSupport == LanguageFeature.State.ENABLED_WITH_WARNING + + class InModule(element: PsiElement, coroutineSupport: LanguageFeature.State) : ChangeCoroutineSupportFix(element, coroutineSupport) { + override fun getText() = "${super.getText()} in the current module" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return + + findApplicableConfigurator(module).changeCoroutineConfiguration(module, coroutineSupport) + } + } + + class InProject(element: PsiElement, coroutineSupport: LanguageFeature.State) : ChangeCoroutineSupportFix(element, coroutineSupport) { + override fun getText() = "${super.getText()} in the project" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + if (coroutineSupportEnabled) { + if (!checkUpdateRuntime(project, LanguageFeature.Coroutines.sinceApiVersion)) return + } + + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { + coroutinesState = when (coroutineSupport) { + LanguageFeature.State.ENABLED -> CommonCompilerArguments.ENABLE + LanguageFeature.State.ENABLED_WITH_WARNING -> CommonCompilerArguments.WARN + LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> CommonCompilerArguments.ERROR + } + } + ProjectRootManagerEx.getInstanceEx(project).makeRootsChange({}, false, true) + } + + } + + override fun getFamilyName() = "Enable/Disable coroutine support" + + override fun getText(): String { + return getFixText(coroutineSupport) + } + + companion object : KotlinIntentionActionsFactory() { + fun getFixText(state: LanguageFeature.State): String { + return when (state) { + LanguageFeature.State.ENABLED -> "Enable coroutine support" + LanguageFeature.State.ENABLED_WITH_WARNING -> "Enable coroutine support (with warning)" + LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> "Disable coroutine support" + } + } + + override fun doCreateActions(diagnostic: Diagnostic): List { + val newCoroutineSupports = when (diagnostic.factory) { + Errors.EXPERIMENTAL_FEATURE_ERROR -> { + if (Errors.EXPERIMENTAL_FEATURE_ERROR.cast(diagnostic).a.first != LanguageFeature.Coroutines) return emptyList() + listOf(LanguageFeature.State.ENABLED_WITH_WARNING, LanguageFeature.State.ENABLED) + } + Errors.EXPERIMENTAL_FEATURE_WARNING -> { + if (Errors.EXPERIMENTAL_FEATURE_WARNING.cast(diagnostic).a.first != LanguageFeature.Coroutines) return emptyList() + listOf(LanguageFeature.State.ENABLED, LanguageFeature.State.ENABLED_WITH_ERROR) + } + else -> return emptyList() + } + val module = ModuleUtilCore.findModuleForPsiElement(diagnostic.psiElement) ?: return emptyList() + val facetSettings = KotlinFacet.get(module)?.configuration?.settings + + val configureInProject = (facetSettings == null || facetSettings.useProjectSettings) && + module.getBuildSystemType() == BuildSystemType.JPS + val quickFixConstructor: (PsiElement, LanguageFeature.State) -> ChangeCoroutineSupportFix = + if (configureInProject) ::InProject else ::InModule + return newCoroutineSupports.map { quickFixConstructor(diagnostic.psiElement, it) } + } + } +} diff --git a/idea/idea-maven/build.gradle.kts.as32 b/idea/idea-maven/build.gradle.kts.as32 new file mode 100644 index 00000000000..953095ab0bc --- /dev/null +++ b/idea/idea-maven/build.gradle.kts.as32 @@ -0,0 +1,62 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compile(project(":core:util.runtime")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":compiler:util")) + compile(project(":compiler:cli-common")) + compile(project(":kotlin-build-common")) + + compile(project(":js:js.frontend")) + + compile(project(":idea")) + compile(project(":idea:idea-jvm")) + compile(project(":idea:idea-jps-common")) + + compileOnly(intellijDep()) + excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) } + + testCompile(projectTests(":idea")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(projectTests(":idea:idea-test-framework")) + + testCompileOnly(intellijDep()) + //testCompileOnly(intellijPluginDep("maven")) + + testRuntime(projectDist(":kotlin-reflect")) + testRuntime(project(":idea:idea-jvm")) + testRuntime(project(":idea:idea-android")) + testRuntime(project(":plugins:android-extensions-ide")) + testRuntime(project(":plugins:lint")) + testRuntime(project(":sam-with-receiver-ide-plugin")) + testRuntime(project(":allopen-ide-plugin")) + testRuntime(project(":noarg-ide-plugin")) + + testRuntime(intellijDep()) + // TODO: the order of the plugins matters here, consider avoiding order-dependency + testRuntime(intellijPluginDep("junit")) + testRuntime(intellijPluginDep("testng")) + testRuntime(intellijPluginDep("properties")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("coverage")) + //testRuntime(intellijPluginDep("maven")) + testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) +} + +sourceSets { + "main" { /*projectDefault()*/ } + "test" { /*projectDefault()*/ } +} + +testsJar() + +projectTest { + workingDir = rootDir +} diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt.as32 b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt.as32 new file mode 100644 index 00000000000..617b5bb2742 --- /dev/null +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt.as32 @@ -0,0 +1,326 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.maven.configuration + +import com.intellij.codeInsight.CodeInsightUtilCore +import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix +import com.intellij.ide.actions.OpenFileAction +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.fileEditor.OpenFileDescriptor +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ExternalLibraryDescriptor +import com.intellij.openapi.roots.JavaProjectModelModificationService +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.vfs.WritingAccessProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import com.intellij.psi.search.FileTypeIndex +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.xml.XmlFile +import org.jetbrains.idea.maven.dom.MavenDomUtil +import org.jetbrains.idea.maven.dom.model.MavenDomPlugin +import org.jetbrains.idea.maven.model.MavenId +import org.jetbrains.idea.maven.project.MavenProjectsManager +import org.jetbrains.idea.maven.utils.MavenArtifactScope +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.config.CoroutineSupport +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.idea.configuration.* +import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion +import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion +import org.jetbrains.kotlin.idea.maven.* +import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor + +abstract class KotlinMavenConfigurator +protected constructor( + private val testArtifactId: String?, + private val addJunit: Boolean, + override val name: String, + override val presentableText: String +) : KotlinProjectConfigurator { + + override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus { + val module = moduleSourceRootGroup.baseModule + if (module.getBuildSystemType() != Maven) + return ConfigureKotlinStatus.NON_APPLICABLE + + val psi = runReadAction { findModulePomFile(module) } + if (psi == null + || !psi.isValid + || psi !is XmlFile + || psi.virtualFile == null) { + return ConfigureKotlinStatus.BROKEN + } + + if (isKotlinModule(module)) { + return runReadAction { checkKotlinPlugin(module) } + } + return ConfigureKotlinStatus.CAN_BE_CONFIGURED + } + + private fun checkKotlinPlugin(module: Module): ConfigureKotlinStatus { + val psi = findModulePomFile(module) as? XmlFile ?: return ConfigureKotlinStatus.BROKEN + val pom = PomFile.forFileOrNull(psi) ?: return ConfigureKotlinStatus.NON_APPLICABLE + + if (hasKotlinPlugin(pom)) { + return ConfigureKotlinStatus.CONFIGURED + } + + val mavenProjectsManager = MavenProjectsManager.getInstance(module.project) + val mavenProject = mavenProjectsManager.findProject(module) ?: return ConfigureKotlinStatus.BROKEN + + val kotlinPluginId = kotlinPluginId(null) + val kotlinPlugin = mavenProject.plugins.find { it.mavenId.equals(kotlinPluginId.groupId, kotlinPluginId.artifactId) } + ?: return ConfigureKotlinStatus.CAN_BE_CONFIGURED + + if (kotlinPlugin.executions.any { it.goals.any(this::isRelevantGoal) }) { + return ConfigureKotlinStatus.CONFIGURED + } + + return ConfigureKotlinStatus.CAN_BE_CONFIGURED + } + + private fun hasKotlinPlugin(pom: PomFile): Boolean { + val plugin = pom.findPlugin(kotlinPluginId(null)) ?: return false + + return plugin.executions.executions.any { + it.goals.goals.any { isRelevantGoal(it.stringValue ?: "") } + } + } + + override fun configure(project: Project, excludeModules: Collection) { + val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules, getMinimumSupportedVersion()) + + dialog.show() + if (!dialog.isOK) return + + WriteCommandAction.runWriteCommandAction(project) { + val collector = createConfigureKotlinNotificationCollector(project) + for (module in excludeMavenChildrenModules(project, dialog.modulesToConfigure)) { + val file = findModulePomFile(module) + if (file != null && canConfigureFile(file)) { + configureModule(module, file, dialog.kotlinVersion, collector) + OpenFileAction.openFile(file.virtualFile, project) + } else { + showErrorMessage(project, "Cannot find pom.xml for module " + module.name) + } + } + collector.showNotification() + } + } + + protected open fun getMinimumSupportedVersion() = "1.0.0" + + protected abstract fun isKotlinModule(module: Module): Boolean + protected abstract fun isRelevantGoal(goalName: String): Boolean + + protected abstract fun createExecutions(pomFile: PomFile, kotlinPlugin: MavenDomPlugin, module: Module) + protected abstract fun getStdlibArtifactId(module: Module, version: String): String + + open fun configureModule(module: Module, file: PsiFile, version: String, collector: NotificationMessageCollector): Boolean = + changePomFile(module, file, version, collector) + + private fun changePomFile( + module: Module, + file: PsiFile, + version: String, + collector: NotificationMessageCollector + ): Boolean { + val virtualFile = file.virtualFile ?: error("Virtual file should exists for psi file " + file.name) + val domModel = MavenDomUtil.getMavenDomProjectModel(module.project, virtualFile) + if (domModel == null) { + showErrorMessage(module.project, null) + return false + } + + val pom = PomFile.forFileOrNull(file as XmlFile) ?: return false + pom.addProperty(KOTLIN_VERSION_PROPERTY, version) + + pom.addDependency( + MavenId(GROUP_ID, getStdlibArtifactId(module, version), "\${$KOTLIN_VERSION_PROPERTY}"), + MavenArtifactScope.COMPILE, + null, + false, + null + ) + if (testArtifactId != null) { + pom.addDependency(MavenId(GROUP_ID, testArtifactId, "\${$KOTLIN_VERSION_PROPERTY}"), MavenArtifactScope.TEST, null, false, null) + } + if (addJunit) { + // TODO currently it is always disabled: junit version selection could be shown in the configurator dialog + pom.addDependency(MavenId("junit", "junit", "4.12"), MavenArtifactScope.TEST, null, false, null) + } + + val repositoryDescription = getRepositoryForVersion(version) + if (repositoryDescription != null) { + pom.addLibraryRepository(repositoryDescription) + pom.addPluginRepository(repositoryDescription) + } + + val plugin = pom.addPlugin(MavenId(GROUP_ID, MAVEN_PLUGIN_ID, "\${$KOTLIN_VERSION_PROPERTY}")) + createExecutions(pom, plugin, module) + + configurePlugin(pom, plugin, module, version) + + CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file) + + collector.addMessage(virtualFile.path + " was modified") + return true + } + + protected open fun configurePlugin(pom: PomFile, plugin: MavenDomPlugin, module: Module, version: String) { + } + + protected fun createExecution( + pomFile: PomFile, + kotlinPlugin: MavenDomPlugin, + executionId: String, + goalName: String, + module: Module, + isTest: Boolean + ) { + pomFile.addKotlinExecution(module, kotlinPlugin, executionId, PomFile.getPhase(false, isTest), isTest, listOf(goalName)) + + if (hasJavaFiles(module)) { + pomFile.addJavacExecutions(module, kotlinPlugin) + } + } + + override fun updateLanguageVersion( + module: Module, + languageVersion: String?, + apiVersion: String?, + requiredStdlibVersion: ApiVersion, + forTests: Boolean + ) { + fun doUpdateMavenLanguageVersion(): PsiElement? { + val psi = KotlinMavenConfigurator.findModulePomFile(module) as? XmlFile ?: return null + val pom = PomFile.forFileOrNull(psi) ?: return null + return pom.changeLanguageVersion( + languageVersion, + apiVersion + ) + } + + val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion -> + runtimeVersion < requiredStdlibVersion + } ?: false + + if (runtimeUpdateRequired) { + Messages.showErrorDialog( + module.project, + "This language feature requires version $requiredStdlibVersion or later of the Kotlin runtime library. " + + "Please update the version in your build script.", + "Update Language Version" + ) + return + } + + val element = doUpdateMavenLanguageVersion() + if (element == null) { + Messages.showErrorDialog( + module.project, + "Failed to update.pom.xml. Please update the file manually.", + "Update Language Version" + ) + } else { + OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true) + } + } + + override fun addLibraryDependency( + module: Module, + element: PsiElement, + library: ExternalLibraryDescriptor, + libraryJarDescriptors: List + ) { + val scope = OrderEntryFix.suggestScopeByLocation(module, element) + JavaProjectModelModificationService.getInstance(module.project).addDependency(module, library, scope) + } + + override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) { + val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED && + (getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false) + + val messageTitle = ChangeCoroutineSupportFix.getFixText(state) + if (runtimeUpdateRequired) { + Messages.showErrorDialog( + module.project, + "Coroutines support requires version 1.1 or later of the Kotlin runtime library. " + + "Please update the version in your build script.", + messageTitle + ) + return + } + + val element = changeMavenCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state), messageTitle) + + if (element != null) { + OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true) + } + + } + + private fun changeMavenCoroutineConfiguration(module: Module, value: String, messageTitle: String): PsiElement? { + fun doChangeMavenCoroutineConfiguration(): PsiElement? { + val psi = KotlinMavenConfigurator.findModulePomFile(module) as? XmlFile ?: return null + val pom = PomFile.forFileOrNull(psi) ?: return null + return pom.changeCoroutineConfiguration(value) + } + + val element = doChangeMavenCoroutineConfiguration() + if (element == null) { + Messages.showErrorDialog( + module.project, + "Failed to update.pom.xml. Please update the file manually.", + messageTitle + ) + } + return element + } + + companion object { + const val GROUP_ID = "org.jetbrains.kotlin" + const val MAVEN_PLUGIN_ID = "kotlin-maven-plugin" + private const val KOTLIN_VERSION_PROPERTY = "kotlin.version" + + private fun hasJavaFiles(module: Module): Boolean { + return !FileTypeIndex.getFiles(JavaFileType.INSTANCE, GlobalSearchScope.moduleScope(module)).isEmpty() + } + + private fun findModulePomFile(module: Module): PsiFile? { + val files = MavenProjectsManager.getInstance(module.project).projectsFiles + for (file in files) { + val fileModule = ModuleUtilCore.findModuleForFile(file, module.project) + if (module != fileModule) continue + val psiFile = PsiManager.getInstance(module.project).findFile(file) ?: continue + if (!MavenDomUtil.isProjectFile(psiFile)) continue + return psiFile + } + return null + } + + private fun canConfigureFile(file: PsiFile): Boolean { + return WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null) + } + + private fun showErrorMessage(project: Project, message: String?) { + Messages.showErrorDialog( + project, + "Couldn't configure kotlin-maven plugin automatically.
" + + (if (message != null) "$message
" else "") + + "See manual installation instructions here.", + "Configure Kotlin-Maven Plugin" + ) + } + } +} diff --git a/idea/idea-maven/testData/languageFeature/enableCoroutines/pom.xml.after.as32 b/idea/idea-maven/testData/languageFeature/enableCoroutines/pom.xml.after.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/enableCoroutines/pom.xml.as32 b/idea/idea-maven/testData/languageFeature/enableCoroutines/pom.xml.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/enableCoroutines/src.kt.as32 b/idea/idea-maven/testData/languageFeature/enableCoroutines/src.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateApiVersion/pom.xml.after.as32 b/idea/idea-maven/testData/languageFeature/updateApiVersion/pom.xml.after.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateApiVersion/pom.xml.as32 b/idea/idea-maven/testData/languageFeature/updateApiVersion/pom.xml.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateApiVersion/src.kt.as32 b/idea/idea-maven/testData/languageFeature/updateApiVersion/src.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageAndApiVersion/pom.xml.after.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageAndApiVersion/pom.xml.after.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageAndApiVersion/pom.xml.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageAndApiVersion/pom.xml.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageAndApiVersion/src.kt.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageAndApiVersion/src.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageVersion/pom.xml.after.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageVersion/pom.xml.after.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageVersion/pom.xml.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageVersion/pom.xml.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageVersion/src.kt.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageVersion/src.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageVersionProperty/pom.xml.after.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageVersionProperty/pom.xml.after.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageVersionProperty/pom.xml.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageVersionProperty/pom.xml.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/idea-maven/testData/languageFeature/updateLanguageVersionProperty/src.kt.as32 b/idea/idea-maven/testData/languageFeature/updateLanguageVersionProperty/src.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/src/META-INF/android-lint.xml.as32 b/idea/src/META-INF/android-lint.xml.as32 new file mode 100644 index 00000000000..5a029687008 --- /dev/null +++ b/idea/src/META-INF/android-lint.xml.as32 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/idea/src/META-INF/android.xml.as32 b/idea/src/META-INF/android.xml.as32 new file mode 100644 index 00000000000..babdbcbcc4b --- /dev/null +++ b/idea/src/META-INF/android.xml.as32 @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + org.jetbrains.kotlin.android.intention.KotlinAndroidAddStringResource + Kotlin Android + + + + + + org.jetbrains.kotlin.android.intention.AddActivityToManifest + Kotlin Android + + + + org.jetbrains.kotlin.android.intention.AddServiceToManifest + Kotlin Android + + + + org.jetbrains.kotlin.android.intention.AddBroadcastReceiverToManifest + Kotlin Android + + + org.jetbrains.kotlin.android.intention.ImplementParcelableAction + Kotlin Android + + + + org.jetbrains.kotlin.android.intention.RemoveParcelableAction + Kotlin Android + + + + org.jetbrains.kotlin.android.intention.RedoParcelableAction + Kotlin Android + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/idea/src/META-INF/plugin.xml.as32 b/idea/src/META-INF/plugin.xml.as32 index 1f532389d9c..809b48f68a9 100644 --- a/idea/src/META-INF/plugin.xml.as32 +++ b/idea/src/META-INF/plugin.xml.as32 @@ -6,9 +6,10 @@ @snapshot@ JetBrains - + com.intellij.modules.platform + com.intellij.modules.remoteServers org.jetbrains.kotlin.native.platform.deps diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as32 b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as32 new file mode 100644 index 00000000000..42021706795 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.as32 @@ -0,0 +1,218 @@ +/* + * Copyright 2010-2015 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.actions + +import com.intellij.codeInsight.navigation.NavigationUtil +import com.intellij.ide.scratch.ScratchFileService +import com.intellij.ide.scratch.ScratchRootType +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.ui.ex.MessagesEx +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileVisitor +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiErrorElement +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiManager +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices +import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor +import org.jetbrains.kotlin.idea.refactoring.toPsiFile +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.j2k.ConverterSettings +import org.jetbrains.kotlin.j2k.JavaToKotlinConverter +import org.jetbrains.kotlin.psi.KtFile +import java.io.File +import java.io.IOException +import java.util.* + +class JavaToKotlinAction : AnAction() { + companion object { + private fun uniqueKotlinFileName(javaFile: VirtualFile): String { + val ioFile = File(javaFile.path.replace('/', File.separatorChar)) + + var i = 0 + while (true) { + val fileName = javaFile.nameWithoutExtension + (if (i > 0) i else "") + ".kt" + if (!ioFile.resolveSibling(fileName).exists()) return fileName + i++ + } + } + + val title = "Convert Java to Kotlin" + + private fun saveResults(javaFiles: List, convertedTexts: List): List { + val result = ArrayList() + for ((psiFile, text) in javaFiles.zip(convertedTexts)) { + try { + val document = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile) + if (document == null) { + MessagesEx.error(psiFile.project, "Failed to save conversion result: couldn't find document for " + psiFile.name).showLater() + continue + } + document.replaceString(0, document.textLength, text) + FileDocumentManager.getInstance().saveDocument(document) + + val virtualFile = psiFile.virtualFile + if (ScratchRootType.getInstance().containsFile(virtualFile)) { + val mapping = ScratchFileService.getInstance().scratchesMapping + mapping.setMapping(virtualFile, KotlinFileType.INSTANCE.language) + } + else { + val fileName = uniqueKotlinFileName(virtualFile) + virtualFile.rename(this, fileName) + } + } + catch (e: IOException) { + MessagesEx.error(psiFile.project, e.message ?: "").showLater() + } + } + return result + } + + fun convertFiles(javaFiles: List, project: Project, + enableExternalCodeProcessing: Boolean = true, + askExternalCodeProcessing: Boolean = true): List { + var converterResult: JavaToKotlinConverter.FilesResult? = null + fun convert() { + val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices) + converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator!!) + } + + + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously( + ::convert, + title, + true, + project)) return emptyList() + + + var externalCodeUpdate: (() -> Unit)? = null + + if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) { + val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?" + if (!askExternalCodeProcessing || (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK)) { + ProgressManager.getInstance().runProcessWithProgressSynchronously( + { + runReadAction { + externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator!!) + } + }, + title, + true, + project) + } + } + + return project.executeWriteCommand("Convert files from Java to Kotlin", null) { + CommandProcessor.getInstance().markCurrentCommandAsGlobal(project) + + val newFiles = saveResults(javaFiles, converterResult!!.results) + + externalCodeUpdate?.invoke() + + PsiDocumentManager.getInstance(project).commitAllDocuments() + + newFiles.singleOrNull()?.let { + FileEditorManager.getInstance(project).openFile(it, true) + } + + newFiles.map { it.toPsiFile(project) as KtFile } + } + } + } + + override fun actionPerformed(e: AnActionEvent) { + val javaFiles = selectedJavaFiles(e).filter { it.isWritable }.toList() + val project = CommonDataKeys.PROJECT.getData(e.dataContext)!! + + val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull() + + if (firstSyntaxError != null) { + val count = javaFiles.filter { PsiTreeUtil.hasErrorElements(it) }.count() + val question = firstSyntaxError.containingFile.name + + (if (count > 1) " and ${count - 1} more Java files" else " file") + + " contain syntax errors, the conversion result may be incorrect" + + val okText = "Investigate Errors" + val cancelText = "Proceed with Conversion" + if (Messages.showOkCancelDialog( + project, + question, + title, + okText, + cancelText, + Messages.getWarningIcon() + ) == Messages.OK) { + NavigationUtil.activateFileWithPsiElement(firstSyntaxError.navigationElement) + return + } + } + + convertFiles(javaFiles, project) + } + + override fun update(e: AnActionEvent) { + val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return + val project = e.project ?: return + + e.presentation.isEnabled = isAnyJavaFileSelected(project, virtualFiles) + } + + private fun isAnyJavaFileSelected(project: Project, files: Array): Boolean { + val manager = PsiManager.getInstance(project) + + if (files.any { manager.findFile(it) is PsiJavaFile && it.isWritable }) return true + return files.any { it.isDirectory && isAnyJavaFileSelected(project, it.children) } + } + + private fun selectedJavaFiles(e: AnActionEvent): Sequence { + val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf() + val project = e.project ?: return sequenceOf() + return allJavaFiles(virtualFiles, project) + } + + private fun allJavaFiles(filesOrDirs: Array, project: Project): Sequence { + val manager = PsiManager.getInstance(project) + return allFiles(filesOrDirs) + .asSequence() + .mapNotNull { manager.findFile(it) as? PsiJavaFile } + } + + private fun allFiles(filesOrDirs: Array): Collection { + val result = ArrayList() + for (file in filesOrDirs) { + VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor() { + override fun visitFile(file: VirtualFile): Boolean { + result.add(file) + return true + } + }) + } + return result + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 new file mode 100644 index 00000000000..4b90d7ba67c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 @@ -0,0 +1,282 @@ +/* + * 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.facet + +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.projectRoots.JavaSdk +import com.intellij.openapi.projectRoots.JavaSdkVersion +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.roots.ModuleRootModel +import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.util.CachedValueProvider +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.cli.common.arguments.* +import org.jetbrains.kotlin.compilerRunner.ArgumentUtils +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.idea.caches.project.* +import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings +import org.jetbrains.kotlin.idea.framework.KotlinSdkType +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.idea.versions.* +import kotlin.reflect.KProperty1 + +private fun getDefaultTargetPlatform(module: Module, rootModel: ModuleRootModel?): TargetPlatformKind<*> { + for (platform in TargetPlatformKind.ALL_PLATFORMS) { + if (getRuntimeLibraryVersions(module, rootModel, platform).isNotEmpty()) { + return platform + } + } + + val sdk = ((rootModel ?: ModuleRootManager.getInstance(module))).sdk + val sdkVersion = (sdk?.sdkType as? JavaSdk)?.getVersion(sdk!!) + return when { + sdkVersion == null || sdkVersion >= JavaSdkVersion.JDK_1_8 -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_8] + else -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6] + } +} + +fun KotlinFacetSettings.initializeIfNeeded( + module: Module, + rootModel: ModuleRootModel?, + platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies + languageVersion: String? = null +) { + val project = module.project + + val shouldInferLanguageLevel = languageLevel == null + val shouldInferAPILevel = apiLevel == null + + if (compilerSettings == null) { + compilerSettings = KotlinCompilerSettings.getInstance(project).settings + } + + val commonArguments = KotlinCommonCompilerArgumentsHolder.getInstance(module.project).settings + + if (compilerArguments == null) { + val targetPlatformKind = platformKind ?: getDefaultTargetPlatform(module, rootModel) + compilerArguments = targetPlatformKind.createCompilerArguments { + targetPlatformKind.getPlatformCompilerArgumentsByProject(module.project)?.let { mergeBeans(it, this) } + mergeBeans(commonArguments, this) + } + } + + if (shouldInferLanguageLevel) { + languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null) + ?: getDefaultLanguageLevel(module, languageVersion) + } + + if (shouldInferAPILevel) { + apiLevel = if (useProjectSettings) { + LanguageVersion.fromVersionString(commonArguments.apiVersion) ?: languageLevel + } + else { + languageLevel!!.coerceAtMost(getLibraryLanguageLevel(module, rootModel, targetPlatformKind)) + } + } +} + +fun TargetPlatformKind<*>.getPlatformCompilerArgumentsByProject(project: Project): CommonCompilerArguments? { + return when (this) { + is TargetPlatformKind.Jvm -> Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings + is TargetPlatformKind.JavaScript -> Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings + else -> null + } +} + +val TargetPlatformKind<*>.mavenLibraryIds: List + get() = when (this) { + is TargetPlatformKind.Jvm -> listOf(MAVEN_STDLIB_ID, MAVEN_STDLIB_ID_JRE7, MAVEN_STDLIB_ID_JDK7, MAVEN_STDLIB_ID_JRE8, MAVEN_STDLIB_ID_JDK8) + is TargetPlatformKind.JavaScript -> listOf(MAVEN_JS_STDLIB_ID, MAVEN_OLD_JS_STDLIB_ID) + is TargetPlatformKind.Common -> listOf(MAVEN_COMMON_STDLIB_ID) + } + +val mavenLibraryIdToPlatform: Map> by lazy { + TargetPlatformKind.ALL_PLATFORMS + .flatMap { platform -> platform.mavenLibraryIds.map { it to platform } } + .sortedByDescending { it.first.length } + .toMap() +} + +fun Module.getOrCreateFacet(modelsProvider: IdeModifiableModelsProvider, + useProjectSettings: Boolean, + commitModel: Boolean = false): KotlinFacet { + val facetModel = modelsProvider.getModifiableFacetModel(this) + + val facet = facetModel.findFacet(KotlinFacetType.TYPE_ID, KotlinFacetType.INSTANCE.defaultFacetName) + ?: with(KotlinFacetType.INSTANCE) { createFacet(this@getOrCreateFacet, defaultFacetName, createDefaultConfiguration(), null) } + .apply { facetModel.addFacet(this) } + facet.configuration.settings.useProjectSettings = useProjectSettings + if (commitModel) { + runWriteAction { + facetModel.commit() + } + } + return facet +} + +fun KotlinFacet.configureFacet( + compilerVersion: String, + coroutineSupport: LanguageFeature.State, + platformKind: TargetPlatformKind<*>?, // if null, detect by module dependencies + modelsProvider: IdeModifiableModelsProvider +) { + val module = module + with(configuration.settings) { + compilerArguments = null + compilerSettings = null + initializeIfNeeded( + module, + modelsProvider.getModifiableRootModel(module), + platformKind, + compilerVersion + ) + val apiLevel = apiLevel + val languageLevel = languageLevel + if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) { + this.apiLevel = languageLevel + } + this.coroutineSupport = coroutineSupport + } +} + +fun KotlinFacet.noVersionAutoAdvance() { + configuration.settings.compilerArguments?.let { + it.autoAdvanceLanguageVersion = false + it.autoAdvanceApiVersion = false + } +} + +// "Primary" fields are written to argument beans directly and thus not presented in the "additional arguments" string +// Update these lists when facet/project settings UI changes +val commonUIExposedFields = listOf(CommonCompilerArguments::languageVersion.name, + CommonCompilerArguments::apiVersion.name, + CommonCompilerArguments::suppressWarnings.name, + CommonCompilerArguments::coroutinesState.name) +private val commonUIHiddenFields = listOf(CommonCompilerArguments::pluginClasspaths.name, + CommonCompilerArguments::pluginOptions.name) +private val commonPrimaryFields = commonUIExposedFields + commonUIHiddenFields + +private val jvmSpecificUIExposedFields = listOf(K2JVMCompilerArguments::jvmTarget.name, + K2JVMCompilerArguments::destination.name, + K2JVMCompilerArguments::classpath.name) +val jvmUIExposedFields = commonUIExposedFields + jvmSpecificUIExposedFields +private val jvmPrimaryFields = commonPrimaryFields + jvmSpecificUIExposedFields + +private val jsSpecificUIExposedFields = listOf(K2JSCompilerArguments::sourceMap.name, + K2JSCompilerArguments::sourceMapPrefix.name, + K2JSCompilerArguments::sourceMapEmbedSources.name, + K2JSCompilerArguments::outputPrefix.name, + K2JSCompilerArguments::outputPostfix.name, + K2JSCompilerArguments::moduleKind.name) +val jsUIExposedFields = commonUIExposedFields + jsSpecificUIExposedFields +private val jsPrimaryFields = commonPrimaryFields + jsSpecificUIExposedFields + +private val metadataSpecificUIExposedFields = listOf(K2MetadataCompilerArguments::destination.name, + K2MetadataCompilerArguments::classpath.name) +val metadataUIExposedFields = commonUIExposedFields + metadataSpecificUIExposedFields +private val metadataPrimaryFields = commonPrimaryFields + metadataSpecificUIExposedFields + +private val CommonCompilerArguments.primaryFields: List + get() = when (this) { + is K2JVMCompilerArguments -> jvmPrimaryFields + is K2JSCompilerArguments -> jsPrimaryFields + is K2MetadataCompilerArguments -> metadataPrimaryFields + else -> commonPrimaryFields + } + +private val CommonCompilerArguments.ignoredFields: List + get() = when (this) { + is K2JVMCompilerArguments -> listOf(K2JVMCompilerArguments::noJdk.name, K2JVMCompilerArguments::jdkHome.name) + else -> emptyList() + } + +private fun Module.configureSdkIfPossible(compilerArguments: CommonCompilerArguments, modelsProvider: IdeModifiableModelsProvider) { + val allSdks = ProjectJdkTable.getInstance().allJdks + val sdk = if (compilerArguments is K2JVMCompilerArguments) { + val jdkHome = compilerArguments.jdkHome ?: return + allSdks.firstOrNull { it.sdkType is JavaSdk && FileUtil.comparePaths(it.homePath, jdkHome) == 0 } ?: return + } else { + allSdks.firstOrNull { it.sdkType is KotlinSdkType } ?: KotlinSdkType.INSTANCE.createSdkWithUniqueName(allSdks.toList()) + } + + modelsProvider.getModifiableRootModel(this).sdk = sdk +} + +fun parseCompilerArgumentsToFacet( + arguments: List, + defaultArguments: List, + kotlinFacet: KotlinFacet, + modelsProvider: IdeModifiableModelsProvider? +) { + with(kotlinFacet.configuration.settings) { + val compilerArguments = this.compilerArguments ?: return + + val defaultCompilerArguments = compilerArguments::class.java.newInstance() + parseCommandLineArguments(defaultArguments, defaultCompilerArguments) + defaultCompilerArguments.convertPathsToSystemIndependent() + + parseCommandLineArguments(arguments, compilerArguments) + + compilerArguments.convertPathsToSystemIndependent() + + // Retain only fields exposed (and not explicitly ignored) in facet configuration editor. + // The rest is combined into string and stored in CompilerSettings.additionalArguments + + if (modelsProvider != null) + kotlinFacet.module.configureSdkIfPossible(compilerArguments, modelsProvider) + + val primaryFields = compilerArguments.primaryFields + val ignoredFields = compilerArguments.ignoredFields + + fun exposeAsAdditionalArgument(property: KProperty1) = + property.name !in primaryFields && property.get(compilerArguments) != property.get(defaultCompilerArguments) + + val additionalArgumentsString = with(compilerArguments::class.java.newInstance()) { + copyFieldsSatisfying(compilerArguments, this) { exposeAsAdditionalArgument(it) && it.name !in ignoredFields } + ArgumentUtils.convertArgumentsToStringList(this).joinToString(separator = " ") { + if (StringUtil.containsWhitespaces(it) || it.startsWith('"')) { + StringUtil.wrapWithDoubleQuote(StringUtil.escapeQuotes(it)) + } else it + } + } + compilerSettings?.additionalArguments = + if (additionalArgumentsString.isNotEmpty()) additionalArgumentsString else CompilerSettings.DEFAULT_ADDITIONAL_ARGUMENTS + + with(compilerArguments::class.java.newInstance()) { + copyFieldsSatisfying(this, compilerArguments) { exposeAsAdditionalArgument(it) || it.name in ignoredFields } + } + + val languageLevel = languageLevel + val apiLevel = apiLevel + if (languageLevel != null && apiLevel != null && apiLevel > languageLevel) { + this.apiLevel = languageLevel + } + + updateMergedArguments() + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/reporter/KotlinReportSubmitter.kt.as32 b/idea/src/org/jetbrains/kotlin/idea/reporter/KotlinReportSubmitter.kt.as32 new file mode 100644 index 00000000000..d35ad586969 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/reporter/KotlinReportSubmitter.kt.as32 @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2015 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.reporter + +import com.intellij.diagnostic.ITNReporter +import com.intellij.openapi.diagnostic.IdeaLoggingEvent +import com.intellij.openapi.diagnostic.SubmittedReportInfo +import com.intellij.openapi.ui.Messages +import com.intellij.util.Consumer +import org.jetbrains.kotlin.idea.KotlinPluginUpdater +import org.jetbrains.kotlin.idea.KotlinPluginUtil +import org.jetbrains.kotlin.idea.PluginUpdateStatus +import org.jetbrains.kotlin.idea.actions.internal.KotlinInternalMode +import java.awt.Component + +/** + * We need to wrap ITNReporter for force showing or errors from kotlin plugin even from released version of IDEA. + */ +class KotlinReportSubmitter : ITNReporter() { + private var hasUpdate = false + private var hasLatestVersion = false + + override fun showErrorInRelease(event: IdeaLoggingEvent): Boolean { + val notificationEnabled = "disabled" != System.getProperty("kotlin.fatal.error.notification", "enabled") + return notificationEnabled && (!hasUpdate || KotlinInternalMode.enabled) + } + + override fun submit(events: Array, additionalInfo: String?, parentComponent: Component?, consumer: Consumer): Boolean { + if (hasUpdate) { + if (KotlinInternalMode.enabled) { + return super.submit(events, additionalInfo, parentComponent, consumer) + } + return true + } + + if (hasLatestVersion) { + return super.submit(events, additionalInfo, parentComponent, consumer) + } + + KotlinPluginUpdater.getInstance().runUpdateCheck { status -> + if (status is PluginUpdateStatus.Update) { + hasUpdate = true + if (parentComponent != null) { + + if (KotlinInternalMode.enabled) { + super.submit(events, additionalInfo, parentComponent, consumer) + } + + val rc = Messages.showDialog(parentComponent, + "You're running Kotlin plugin version ${KotlinPluginUtil.getPluginVersion()}, " + + "while the latest version is ${status.pluginDescriptor.version}", + "Update Kotlin Plugin", + arrayOf("Update", "Ignore"), + 0, Messages.getInformationIcon()) + if (rc == 0) { + KotlinPluginUpdater.getInstance().installPluginUpdate(status) + } + } + } + else { + hasLatestVersion = true + super.submit(events, additionalInfo, parentComponent, consumer) + } + false + } + return true + } +} diff --git a/idea/testData/android/lint/alarm.kt.as32 b/idea/testData/android/lint/alarm.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/apiCheck.kt.as32 b/idea/testData/android/lint/apiCheck.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/callSuper.kt.as32 b/idea/testData/android/lint/callSuper.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/closeCursor.kt.as32 b/idea/testData/android/lint/closeCursor.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/commitFragment.kt.as32 b/idea/testData/android/lint/commitFragment.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/javaPerformance.kt.as32 b/idea/testData/android/lint/javaPerformance.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/javaScriptInterface.kt.as32 b/idea/testData/android/lint/javaScriptInterface.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/layoutInflation.kt.as32 b/idea/testData/android/lint/layoutInflation.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/log.kt.as32 b/idea/testData/android/lint/log.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/noInternationalSms.kt.as32 b/idea/testData/android/lint/noInternationalSms.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/overrideConcrete.kt.as32 b/idea/testData/android/lint/overrideConcrete.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/parcel.kt.as32 b/idea/testData/android/lint/parcel.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/sdCardTest.kt.as32 b/idea/testData/android/lint/sdCardTest.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/setJavaScriptEnabled.kt.as32 b/idea/testData/android/lint/setJavaScriptEnabled.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/sharedPrefs.kt.as32 b/idea/testData/android/lint/sharedPrefs.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt.as32 b/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/sqlite.kt.as32 b/idea/testData/android/lint/sqlite.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/systemServices.kt.as32 b/idea/testData/android/lint/systemServices.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/toast.kt.as32 b/idea/testData/android/lint/toast.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/valueOf.kt.as32 b/idea/testData/android/lint/valueOf.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/velocityTrackerRecycle.kt.as32 b/idea/testData/android/lint/velocityTrackerRecycle.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/viewConstructor.kt.as32 b/idea/testData/android/lint/viewConstructor.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/viewHolder.kt.as32 b/idea/testData/android/lint/viewHolder.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/wrongAnnotation.kt.as32 b/idea/testData/android/lint/wrongAnnotation.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/wrongImport.kt.as32 b/idea/testData/android/lint/wrongImport.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/android/lint/wrongViewCall.kt.as32 b/idea/testData/android/lint/wrongViewCall.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/B.iml.as32 b/idea/testData/multiFileInspections/kotlinInternalInJava/before/B/B.iml.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/quickfix/typeMismatch/addArrayOfTypeForJavaAnnotation.before.Main.kt.as32 b/idea/testData/quickfix/typeMismatch/addArrayOfTypeForJavaAnnotation.before.Main.kt.as32 new file mode 100644 index 00000000000..9ffb61bd471 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/addArrayOfTypeForJavaAnnotation.before.Main.kt.as32 @@ -0,0 +1,10 @@ +// "class org.jetbrains.kotlin.idea.quickfix.AddArrayOfTypeFix" "false" +// ACTION: Add 'value =' to argument +// ACTION: Create test +// ACTION: Do not show hints for current method +// ACTION: Make internal +// ACTION: Make private +// ACTION: To raw string literal +// ACTION: Do not show hints for current method + +@ArrAnn("123") class My \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt.as32 b/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt.as32 new file mode 100644 index 00000000000..4fa250d12ff --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt.as32 @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2015 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 + +import com.intellij.psi.codeStyle.CodeStyleSettingsManager +import com.intellij.psi.codeStyle.PackageEntry +import com.intellij.testFramework.LightProjectDescriptor +import junit.framework.TestCase +import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +abstract class AbstractImportsTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory() + override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + protected fun doTest(testPath: String) { + val settingManager = CodeStyleSettingsManager.getInstance(project) + val tempSettings = settingManager.currentSettings.clone() + settingManager.setTemporarySettings(tempSettings) + + val codeStyleSettings = KotlinCodeStyleSettings.getInstance(project) + + try { + val fixture = myFixture + val dependencySuffixes = listOf(".dependency.kt", ".dependency.java", ".dependency1.kt", ".dependency2.kt") + for (suffix in dependencySuffixes) { + val dependencyPath = testPath.replace(".kt", suffix) + if (File(dependencyPath).exists()) { + fixture.configureByFile(dependencyPath) + } + } + + fixture.configureByFile(testPath) + + val file = fixture.file as KtFile + + val fileText = file.text + + codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT = InTextDirectivesUtils.getPrefixedInt(fileText, "// NAME_COUNT_TO_USE_STAR_IMPORT:") ?: nameCountToUseStarImportDefault + codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS = InTextDirectivesUtils.getPrefixedInt(fileText, "// NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS:") ?: nameCountToUseStarImportForMembersDefault + codeStyleSettings.IMPORT_NESTED_CLASSES = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// IMPORT_NESTED_CLASSES:") ?: false + + InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PACKAGE_TO_USE_STAR_IMPORTS:").forEach { + codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), false)) + } + InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PACKAGES_TO_USE_STAR_IMPORTS:").forEach { + codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), true)) + } + + val log = project.executeWriteCommand("") { doTest(file) } + + KotlinTestUtils.assertEqualsToFile(File(testPath + ".after"), myFixture.file.text) + if (log != null) { + val logFile = File(testPath + ".log") + if (log.isNotEmpty()) { + KotlinTestUtils.assertEqualsToFile(logFile, log) + } + else { + TestCase.assertFalse(logFile.exists()) + } + } + } + finally { + settingManager.dropTemporarySettings() + } + } + + // returns test log + protected abstract fun doTest(file: KtFile): String? + + protected open val nameCountToUseStarImportDefault: Int + get() = 1 + + protected open val nameCountToUseStarImportForMembersDefault: Int + get() = 3 +} \ No newline at end of file diff --git a/j2k/build.gradle.kts.as32 b/j2k/build.gradle.kts.as32 new file mode 100644 index 00000000000..562c44d1a74 --- /dev/null +++ b/j2k/build.gradle.kts.as32 @@ -0,0 +1,75 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testRuntime(intellijDep()) + + compile(projectDist(":kotlin-stdlib")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":compiler:light-classes")) + compile(project(":compiler:util")) + compileOnly(intellijCoreDep()) { includeJars("intellij-core") } + + testCompile(project(":idea")) + testCompile(projectTests(":idea:idea-test-framework")) + testCompile(project(":compiler:light-classes")) + testCompile(projectDist(":kotlin-test:kotlin-test-junit")) + testCompile(commonDep("junit:junit")) + testCompileOnly(intellijDep()) { includeJars("platform-api", "platform-impl") } + + testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false } + testRuntime(project(":idea:idea-jvm")) + testRuntime(project(":idea:idea-android")) + testRuntime(project(":plugins:android-extensions-ide")) + testRuntime(project(":sam-with-receiver-ide-plugin")) + testRuntime(project(":allopen-ide-plugin")) + testRuntime(project(":noarg-ide-plugin")) + testRuntime(intellijPluginDep("properties")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("coverage")) + //testRuntime(intellijPluginDep("maven")) + testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) + testRuntime(intellijPluginDep("junit")) + testRuntime(intellijPluginDep("testng")) + testRuntime(intellijPluginDep("IntelliLang")) + testRuntime(intellijPluginDep("testng")) + testRuntime(intellijPluginDep("copyright")) + testRuntime(intellijPluginDep("properties")) + testRuntime(intellijPluginDep("java-i18n")) + testRuntime(intellijPluginDep("java-decompiler")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +projectTest { + dependsOn(":dist") + workingDir = rootDir +} + +testsJar() + + +val testForWebDemo by task { + include("**/*JavaToKotlinConverterForWebDemoTestGenerated*") + classpath = the().sourceSets["test"].runtimeClasspath + workingDir = rootDir +} +val cleanTestForWebDemo by tasks + +val test: Test by tasks +test.apply { + exclude("**/*JavaToKotlinConverterForWebDemoTestGenerated*") + dependsOn(testForWebDemo) +} +val cleanTest by tasks +cleanTest.dependsOn(cleanTestForWebDemo) + diff --git a/jps-plugin/build.gradle.kts.as32 b/jps-plugin/build.gradle.kts.as32 new file mode 100644 index 00000000000..81e65231f55 --- /dev/null +++ b/jps-plugin/build.gradle.kts.as32 @@ -0,0 +1,51 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +val compilerModules: Array by rootProject.extra + +dependencies { + compile(project(":kotlin-build-common")) + compile(project(":core:descriptors")) + compile(project(":core:descriptors.jvm")) + compile(project(":kotlin-compiler-runner")) + compile(project(":compiler:daemon-common")) + compile(projectRuntimeJar(":kotlin-daemon-client")) + compile(project(":compiler:frontend.java")) + compile(projectRuntimeJar(":kotlin-preloader")) + compile(project(":idea:idea-jps-common")) + compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "platform-api", "util", "asm-all") } + compileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") } + testCompileOnly(project(":kotlin-reflect-api")) + testCompile(project(":compiler:incremental-compilation-impl")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(projectTests(":compiler:incremental-compilation-impl")) + testCompile(commonDep("junit:junit")) + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(projectTests(":kotlin-build-common")) + testCompileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") } + testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "platform-api", "log4j") } + testCompile(intellijDep("jps-build-test")) + compilerModules.forEach { + testRuntime(project(it)) + } + testRuntime(intellijDep()) + testRuntime(projectDist(":kotlin-reflect")) +} + +sourceSets { + "main" { projectDefault() } + "test" { + /*java.srcDirs("jps-tests/test" + /*, "kannotator-jps-plugin-test/test"*/ // Obsolete + )*/ + } +} + +projectTest { + dependsOn(":kotlin-compiler:dist") + workingDir = rootDir +} + +testsJar {} diff --git a/plugins/allopen/allopen-ide/src/AllOpenMavenProjectImportHandler.kt.as32 b/plugins/allopen/allopen-ide/src/AllOpenMavenProjectImportHandler.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/android-extensions/android-extensions-idea/build.gradle.kts.as32 b/plugins/android-extensions/android-extensions-idea/build.gradle.kts.as32 new file mode 100644 index 00000000000..fadabf53858 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/build.gradle.kts.as32 @@ -0,0 +1,75 @@ + +description = "Kotlin Android Extensions IDEA" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +jvmTarget = "1.6" + +dependencies { + testRuntime(intellijDep()) + + compile(project(":compiler:util")) + compile(project(":compiler:light-classes")) + compile(project(":idea:idea-core")) + compile(project(":idea")) + compile(project(":idea:idea-jvm")) + compile(project(":idea:idea-gradle")) + compile(project(":plugins:android-extensions-compiler")) + compileOnly(project(":kotlin-android-extensions-runtime")) + compileOnly(intellijPluginDep("android")) + compileOnly(intellijPluginDep("Groovy")) + compileOnly(intellijDep()) + + testCompile(project(":compiler:tests-common")) + testCompile(project(":compiler:cli")) + testCompile(project(":compiler:frontend.java")) + testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false } + testCompile(project(":plugins:kapt3-idea")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(projectTests(":idea")) + testCompile(projectTests(":idea:idea-android")) + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(commonDep("junit:junit")) + testRuntime(projectDist(":kotlin-reflect")) + testCompile(intellijPluginDep("android")) + testCompile(intellijPluginDep("Groovy")) + testCompile(intellijDep()) + + testRuntime(project(":idea:idea-jvm")) + testRuntime(project(":plugins:android-extensions-jps")) + testRuntime(project(":sam-with-receiver-ide-plugin")) + testRuntime(project(":noarg-ide-plugin")) + testRuntime(project(":allopen-ide-plugin")) + testRuntime(project(":plugins:lint")) + testRuntime(intellijPluginDep("junit")) + testRuntime(intellijPluginDep("IntelliLang")) + testRuntime(intellijPluginDep("properties")) + testRuntime(intellijPluginDep("java-i18n")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("java-decompiler")) + //testRuntime(intellijPluginDep("maven")) + testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +testsJar {} + +projectTest { + dependsOn(":kotlin-android-extensions-runtime:dist") + workingDir = rootDir + useAndroidSdk() + useAndroidJar() +} + +runtimeJar() + +ideaPlugin() diff --git a/plugins/android-extensions/android-extensions-jps/test/org/jetbrains/kotlin/jps/build/android/AbstractAndroidJpsTestCase.kt.as32 b/plugins/android-extensions/android-extensions-jps/test/org/jetbrains/kotlin/jps/build/android/AbstractAndroidJpsTestCase.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/android-extensions/android-extensions-jps/test/org/jetbrains/kotlin/jps/build/android/AndroidJpsTestCaseGenerated.java.as32 b/plugins/android-extensions/android-extensions-jps/test/org/jetbrains/kotlin/jps/build/android/AndroidJpsTestCaseGenerated.java.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractMavenImportHandler.kt.as32 b/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractMavenImportHandler.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt.as32 b/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt.as32 new file mode 100644 index 00000000000..c27a38c1523 --- /dev/null +++ b/plugins/kapt3/kapt3-idea/src/org/jetbrains/kotlin/kapt/idea/KaptProjectResolverExtension.kt.as32 @@ -0,0 +1,202 @@ +/* + * 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.kapt.idea + +import com.android.tools.idea.gradle.project.model.AndroidModuleModel +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.* +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.tooling.model.idea.IdeaModule +import org.jetbrains.kotlin.gradle.AbstractKotlinGradleModelBuilder +import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import java.io.File +import java.io.Serializable +import java.lang.Exception +import java.lang.reflect.Modifier + +interface KaptSourceSetModel : Serializable { + val sourceSetName: String + val isTest: Boolean + val generatedSourcesDir: String + val generatedClassesDir: String + val generatedKotlinSourcesDir: String + + val generatedSourcesDirFile get() = generatedSourcesDir.takeIf { it.isNotEmpty() }?.let(::File) + val generatedClassesDirFile get() = generatedClassesDir.takeIf { it.isNotEmpty() }?.let(::File) + val generatedKotlinSourcesDirFile get() = generatedKotlinSourcesDir.takeIf { it.isNotEmpty() }?.let(::File) +} + +class KaptSourceSetModelImpl( + override val sourceSetName: String, + override val isTest: Boolean, + override val generatedSourcesDir: String, + override val generatedClassesDir: String, + override val generatedKotlinSourcesDir: String +) : KaptSourceSetModel + +interface KaptGradleModel : Serializable { + val isEnabled: Boolean + val buildDirectory: File + val sourceSets: List +} + +class KaptGradleModelImpl( + override val isEnabled: Boolean, + override val buildDirectory: File, + override val sourceSets: List +) : KaptGradleModel + +internal typealias AndroidGradleModel = AndroidModuleModel + +@Suppress("unused") +class KaptProjectResolverExtension : AbstractProjectResolverExtension() { + private companion object { + private val LOG = Logger.getInstance(KaptProjectResolverExtension::class.java) + } + + override fun getExtraProjectModelClasses() = setOf(KaptGradleModel::class.java) + override fun getToolingExtensionsClasses() = setOf(KaptModelBuilderService::class.java, Unit::class.java) + + override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode) { + val kaptModel = resolverCtx.getExtraProject(gradleModule, KaptGradleModel::class.java) ?: return + + if (kaptModel.isEnabled) { + for (sourceSet in kaptModel.sourceSets) { + populateAndroidModuleModelIfNeeded(ideModule, sourceSet) + + val sourceSetDataNode = ideModule.findGradleSourceSet(sourceSet.sourceSetName) ?: continue + + fun addSourceSet(path: String, type: ExternalSystemSourceType) { + val contentRootData = ContentRootData(GRADLE_SYSTEM_ID, path) + contentRootData.storePath(type, path) + sourceSetDataNode.createChild(ProjectKeys.CONTENT_ROOT, contentRootData) + } + + val sourceType = if (sourceSet.isTest) ExternalSystemSourceType.TEST_GENERATED else ExternalSystemSourceType.SOURCE_GENERATED + sourceSet.generatedSourcesDirFile?.let { addSourceSet(it.absolutePath, sourceType) } + sourceSet.generatedKotlinSourcesDirFile?.let { addSourceSet(it.absolutePath, sourceType) } + + sourceSet.generatedClassesDirFile?.let { generatedClassesDir -> + val libraryData = LibraryData(GRADLE_SYSTEM_ID, "kaptGeneratedClasses") + libraryData.addPath(LibraryPathType.BINARY, generatedClassesDir.absolutePath) + val libraryDependencyData = LibraryDependencyData(sourceSetDataNode.data, libraryData, LibraryLevel.MODULE) + sourceSetDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData) + } + } + } + + super.populateModuleExtraModels(gradleModule, ideModule) + } + + private fun populateAndroidModuleModelIfNeeded(ideModule: DataNode, sourceSet: KaptSourceSetModel) { + ideModule.findAndroidModuleModel()?.let { androidModelAny -> + // We can cast to AndroidModuleModel cause we already checked in findAndroidModuleModel() that the class exists + + val generatedKotlinSources = sourceSet.generatedKotlinSourcesDirFile ?: return + + val androidModel = androidModelAny.data as? AndroidModuleModel ?: return + val variant = androidModel.findVariantByName(sourceSet.sourceSetName) ?: return + + androidModel.registerExtraGeneratedSourceFolder(generatedKotlinSources) + + // TODO remove this when IDEA eventually migrate to the newer Android plugin + try { + variant.mainArtifact.generatedSourceFolders += generatedKotlinSources + } catch (e: Throwable) { + // There was an error being thrown here, but the code above doesn't work for the newer versions of Android Studio 3 + // (generatedSourceFolders returns a wrapped unmodifiable list), and the thrown exception breaks the import. + // The error will be moved back when I find a work-around for AS3. + } + } + } + + private fun DataNode.findAndroidModuleModel(): DataNode<*>? { + val modelClassName = "com.android.tools.idea.gradle.project.model.AndroidModuleModel" + val node = children.firstOrNull { it.key.dataType == modelClassName } ?: return null + return if (!hasClassInClasspath(modelClassName)) null else node + } + + private fun hasClassInClasspath(name: String): Boolean { + return try { + Class.forName(name) != null + } catch (thr: Throwable) { + false + } + } + + private fun DataNode.findGradleSourceSet(sourceSetName: String): DataNode? { + val moduleName = data.id + for (child in children) { + val gradleSourceSetData = child.data as? GradleSourceSetData ?: continue + if (gradleSourceSetData.id == "$moduleName:$sourceSetName") { + @Suppress("UNCHECKED_CAST") + return child as DataNode? + } + } + + return null + } +} + +class KaptModelBuilderService : AbstractKotlinGradleModelBuilder() { + override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder { + return ErrorMessageBuilder.create(project, e, "Gradle import errors") + .withDescription("Unable to build kotlin-kapt plugin configuration") + } + + override fun canBuild(modelName: String?): Boolean = modelName == KaptGradleModel::class.java.name + + override fun buildAll(modelName: String?, project: Project): Any { + val kaptPlugin: Plugin<*>? = project.plugins.findPlugin("kotlin-kapt") + val kaptIsEnabled = kaptPlugin != null + + val sourceSets = mutableListOf() + + if (kaptIsEnabled) { + project.getAllTasks(false)[project]?.forEach { compileTask -> + if (compileTask.javaClass.name !in kotlinCompileTaskClasses) return@forEach + + val sourceSetName = compileTask.getSourceSetName() + val isTest = sourceSetName.toLowerCase().endsWith("test") + + val kaptGeneratedSourcesDir = getKaptDirectory("getKaptGeneratedSourcesDir", project, sourceSetName) + val kaptGeneratedClassesDir = getKaptDirectory("getKaptGeneratedClassesDir", project, sourceSetName) + val kaptGeneratedKotlinSourcesDir = getKaptDirectory("getKaptGeneratedKotlinSourcesDir", project, sourceSetName) + sourceSets += KaptSourceSetModelImpl( + sourceSetName, isTest, kaptGeneratedSourcesDir, kaptGeneratedClassesDir, kaptGeneratedKotlinSourcesDir) + } + } + + return KaptGradleModelImpl(kaptIsEnabled, project.buildDir, sourceSets) + } + + private fun getKaptDirectory(funName: String, project: Project, sourceSetName: String): String { + val kotlinKaptPlugin = project.plugins.findPlugin("kotlin-kapt") ?: return "" + + val targetMethod = kotlinKaptPlugin::class.java.methods.firstOrNull { + Modifier.isStatic(it.modifiers) && it.name == funName && it.parameterCount == 2 + } ?: return "" + + return (targetMethod(null, project, sourceSetName) as? File)?.absolutePath ?: "" + } +} \ No newline at end of file diff --git a/plugins/noarg/noarg-ide/src/NoArgMavenProjectImportHandler.kt.as32 b/plugins/noarg/noarg-ide/src/NoArgMavenProjectImportHandler.kt.as32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/sam-with-receiver/sam-with-receiver-ide/src/SamWithReceiverMavenProjectImportHandler.kt.as32 b/plugins/sam-with-receiver/sam-with-receiver-ide/src/SamWithReceiverMavenProjectImportHandler.kt.as32 new file mode 100644 index 00000000000..fdc35926df3 --- /dev/null +++ b/plugins/sam-with-receiver/sam-with-receiver-ide/src/SamWithReceiverMavenProjectImportHandler.kt.as32 @@ -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.samWithReceiver.ide + +/* +import org.jetbrains.kotlin.annotation.plugin.ide.AbstractMavenImportHandler +import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedCompilerPluginSetup +import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedCompilerPluginSetup.PluginOption +import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor +import org.jetbrains.kotlin.utils.PathUtil + +class SamWithReceiverMavenProjectImportHandler : AbstractMavenImportHandler() { + private companion object { + val ANNOTATION_PARAMETER_PREFIX = "sam-with-receiver:${SamWithReceiverCommandLineProcessor.ANNOTATION_OPTION.name}=" + } + + override val compilerPluginId = SamWithReceiverCommandLineProcessor.PLUGIN_ID + override val pluginName = "samWithReceiver" + override val mavenPluginArtifactName = "kotlin-maven-sam-with-receiver" + override val pluginJarFileFromIdea = PathUtil.kotlinPathsForIdeaPlugin.allOpenPluginJarPath + + override fun getOptions(enabledCompilerPlugins: List, compilerPluginOptions: List): List? { + if ("sam-with-receiver" !in enabledCompilerPlugins) { + return null + } + + val annotations = mutableListOf() + + for ((presetName, presetAnnotations) in SamWithReceiverCommandLineProcessor.SUPPORTED_PRESETS) { + if (presetName in enabledCompilerPlugins) { + annotations.addAll(presetAnnotations) + } + } + + annotations.addAll(compilerPluginOptions.mapNotNull { text -> + if (!text.startsWith(ANNOTATION_PARAMETER_PREFIX)) return@mapNotNull null + text.substring(ANNOTATION_PARAMETER_PREFIX.length) + }) + + return annotations.map { PluginOption(SamWithReceiverCommandLineProcessor.ANNOTATION_OPTION.name, it) } + } +} +*/ \ No newline at end of file diff --git a/ultimate/build.gradle.kts.as32 b/ultimate/build.gradle.kts.as32 new file mode 100644 index 00000000000..f330b5e05e6 --- /dev/null +++ b/ultimate/build.gradle.kts.as32 @@ -0,0 +1,212 @@ +/* + + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.jvm.tasks.Jar + +description = "Kotlin IDEA Ultimate plugin" + +plugins { + kotlin("jvm") +} + +val ideaProjectResources = project(":idea").the().sourceSets["main"].output.resourcesDir + +evaluationDependsOn(":prepare:idea-plugin") + +val intellijUltimateEnabled : Boolean by rootProject.extra + +val springClasspath by configurations.creating + +dependencies { + if (intellijUltimateEnabled) { + testRuntime(intellijUltimateDep()) + } + + compileOnly(project(":kotlin-reflect-api")) + compile(projectDist(":kotlin-stdlib")) + compile(project(":core:descriptors")) { isTransitive = false } + compile(project(":compiler:psi")) { isTransitive = false } + compile(project(":core:descriptors.jvm")) { isTransitive = false } + compile(project(":core:util.runtime")) { isTransitive = false } + compile(project(":compiler:light-classes")) { isTransitive = false } + compile(project(":compiler:frontend")) { isTransitive = false } + compile(project(":compiler:frontend.java")) { isTransitive = false } + compile(project(":js:js.frontend")) { isTransitive = false } + compile(projectClasses(":idea")) + compile(project(":idea:idea-jvm")) { isTransitive = false } + compile(project(":idea:idea-core")) { isTransitive = false } + compile(project(":idea:ide-common")) { isTransitive = false } + compile(project(":idea:idea-gradle")) { isTransitive = false } + compileOnly(intellijCoreDep()) { includeJars("intellij-core") } + + if (intellijUltimateEnabled) { + compileOnly(intellijUltimatePluginDep("NodeJS")) + compileOnly(intellijUltimateDep()) { includeJars("annotations", "trove4j", "openapi", "platform-api", "platform-impl", "java-api", "java-impl", "idea", "util", "jdom") } + compileOnly(intellijUltimatePluginDep("CSS")) + compileOnly(intellijUltimatePluginDep("DatabaseTools")) + compileOnly(intellijUltimatePluginDep("JavaEE")) + compileOnly(intellijUltimatePluginDep("jsp")) + compileOnly(intellijUltimatePluginDep("PersistenceSupport")) + compileOnly(intellijUltimatePluginDep("Spring")) + compileOnly(intellijUltimatePluginDep("properties")) + compileOnly(intellijUltimatePluginDep("java-i18n")) + compileOnly(intellijUltimatePluginDep("gradle")) + compileOnly(intellijUltimatePluginDep("Groovy")) + compileOnly(intellijUltimatePluginDep("junit")) + compileOnly(intellijUltimatePluginDep("uml")) + compileOnly(intellijUltimatePluginDep("JavaScriptLanguage")) + compileOnly(intellijUltimatePluginDep("JavaScriptDebugger")) + } + + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false } + testCompile(project(":plugins:lint")) { isTransitive = false } + testCompile(project(":idea:idea-jvm")) { isTransitive = false } + testCompile(projectTests(":compiler:tests-common")) + testCompile(projectTests(":idea")) { isTransitive = false } + testCompile(projectTests(":generators:test-generator")) + testCompile(commonDep("junit:junit")) + if (intellijUltimateEnabled) { + testCompileOnly(intellijUltimateDep()) { includeJars("platform-api", "platform-impl", "gson", "annotations", "trove4j", "openapi", "idea", "util", "jdom", rootProject = rootProject) } + } + testCompile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } + + testRuntime(projectDist(":kotlin-reflect")) + testRuntime(project(":kotlin-script-runtime")) + testRuntime(projectRuntimeJar(":kotlin-compiler")) + testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false } + testRuntime(project(":plugins:android-extensions-compiler")) { isTransitive = false } + testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false } + testRuntime(project(":idea:idea-android")) { isTransitive = false } + testRuntime(project(":idea:idea-maven")) { isTransitive = false } + testRuntime(project(":idea:idea-jps-common")) { isTransitive = false } + testRuntime(project(":idea:formatter")) { isTransitive = false } + testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false } + testRuntime(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false } + testRuntime(project(":noarg-ide-plugin")) { isTransitive = false } + testRuntime(project(":kotlin-noarg-compiler-plugin")) { isTransitive = false } + testRuntime(project(":allopen-ide-plugin")) { isTransitive = false } + testRuntime(project(":kotlin-allopen-compiler-plugin")) { isTransitive = false } + testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false } + testRuntime(project(":plugins:uast-kotlin")) + testRuntime(project(":plugins:uast-kotlin-idea")) + testRuntime(intellijPluginDep("smali")) + + if (intellijUltimateEnabled) { + testCompile(intellijUltimatePluginDep("CSS")) + testCompile(intellijUltimatePluginDep("DatabaseTools")) + testCompile(intellijUltimatePluginDep("JavaEE")) + testCompile(intellijUltimatePluginDep("jsp")) + testCompile(intellijUltimatePluginDep("PersistenceSupport")) + testCompile(intellijUltimatePluginDep("Spring")) + testCompile(intellijUltimatePluginDep("uml")) + testCompile(intellijUltimatePluginDep("JavaScriptLanguage")) + testCompile(intellijUltimatePluginDep("JavaScriptDebugger")) + testCompile(intellijUltimatePluginDep("NodeJS")) + testCompile(intellijUltimatePluginDep("properties")) + testCompile(intellijUltimatePluginDep("java-i18n")) + testCompile(intellijUltimatePluginDep("gradle")) + testCompile(intellijUltimatePluginDep("Groovy")) + testCompile(intellijUltimatePluginDep("junit")) + testRuntime(intellijUltimatePluginDep("coverage")) + testRuntime(intellijUltimatePluginDep("maven")) + testRuntime(intellijUltimatePluginDep("android")) + testRuntime(intellijUltimatePluginDep("testng")) + testRuntime(intellijUltimatePluginDep("IntelliLang")) + testRuntime(intellijUltimatePluginDep("copyright")) + testRuntime(intellijUltimatePluginDep("java-decompiler")) + } + + testRuntime(files("${System.getProperty("java.home")}/../lib/tools.jar")) + + springClasspath(commonDep("org.springframework", "spring-core")) + springClasspath(commonDep("org.springframework", "spring-beans")) + springClasspath(commonDep("org.springframework", "spring-context")) + springClasspath(commonDep("org.springframework", "spring-tx")) + springClasspath(commonDep("org.springframework", "spring-web")) +} + +val preparedResources = File(buildDir, "prepResources") + +sourceSets { + "main" { projectDefault() } + "test" { + projectDefault() + resources.srcDir(preparedResources) + } +} + +val ultimatePluginXmlContent: String by lazy { + val sectRex = Regex("""^\s*\s*$""") + File(projectDir, "resources/META-INF/ultimate-plugin.xml") + .readLines() + .filterNot { it.matches(sectRex) } + .joinToString("\n") +} + +val prepareResources by task { + dependsOn(":idea:assemble") + from(ideaProjectResources, { + exclude("META-INF/plugin.xml") + }) + into(preparedResources) +} + +val preparePluginXml by task { + dependsOn(":idea:assemble") + from(ideaProjectResources, { include("META-INF/plugin.xml") }) + into(preparedResources) + filter { + it?.replace("", ultimatePluginXmlContent) + } +} + +val communityPluginProject = ":prepare:idea-plugin" + +val jar = runtimeJar(task("shadowJar")) { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + dependsOn(preparePluginXml) + dependsOn("$communityPluginProject:shadowJar") + val communityPluginJar = project(communityPluginProject).configurations["runtimeJar"].artifacts.files.singleFile + from(zipTree(communityPluginJar), { exclude("META-INF/plugin.xml") }) + from(preparedResources, { include("META-INF/plugin.xml") }) + from(the().sourceSets.getByName("main").output) + archiveName = "kotlin-plugin.jar" +} + +val ideaPluginDir: File by rootProject.extra +val ideaUltimatePluginDir: File by rootProject.extra + +task("ideaUltimatePlugin") { + dependsOn(":ideaPlugin") + into(ideaUltimatePluginDir) + from(ideaPluginDir) { exclude("lib/kotlin-plugin.jar") } + from(jar, { into("lib") }) +} + +task("idea-ultimate-plugin") { + dependsOn("ideaUltimatePlugin") + doFirst { logger.warn("'$name' task is deprecated, use '${dependsOn.last()}' instead") } +} + +task("ideaUltimatePluginTest") { + dependsOn("check") +} + +projectTest { + dependsOn(prepareResources) + dependsOn(preparePluginXml) + workingDir = rootDir + doFirst { + if (intellijUltimateEnabled) { + systemProperty("idea.home.path", intellijUltimateRootDir().canonicalPath) + } + systemProperty("spring.classpath", springClasspath.asPath) + } +} + +val generateTests by generator("org.jetbrains.kotlin.tests.GenerateUltimateTestsKt") + + +*/ \ No newline at end of file diff --git a/ultimate/ultimate-runner/build.gradle.kts.as32 b/ultimate/ultimate-runner/build.gradle.kts.as32 new file mode 100644 index 00000000000..2c0a74d4ec6 --- /dev/null +++ b/ultimate/ultimate-runner/build.gradle.kts.as32 @@ -0,0 +1,32 @@ + +/* + + +plugins { + kotlin("jvm") +} + +dependencies { + compileOnly(project(":idea")) + compileOnly(project(":idea:idea-maven")) + compileOnly(project(":idea:idea-gradle")) + compileOnly(project(":idea:idea-jvm")) + + compile(intellijDep()) + runtimeOnly(files(toolsJar())) +} + +val intellijUltimateEnabled : Boolean by rootProject.extra + +val ideaUltimatePluginDir: File by rootProject.extra +val ideaUltimateSandboxDir: File by rootProject.extra + +if (intellijUltimateEnabled) { + runIdeTask("runUltimate", ideaUltimatePluginDir, ideaUltimateSandboxDir) { + dependsOn(":dist", ":ideaPlugin", ":ultimate:ideaUltimatePlugin") + } +} + + + +*/ \ No newline at end of file diff --git a/update_dependencies.xml.as32 b/update_dependencies.xml.as32 index e677fea186c..5f0535ee273 100644 --- a/update_dependencies.xml.as32 +++ b/update_dependencies.xml.as32 @@ -1,5 +1,4 @@ - diff --git a/versions.gradle.kts.as32 b/versions.gradle.kts.as32 index 53cb5584ea5..dcb297778cc 100644 --- a/versions.gradle.kts.as32 +++ b/versions.gradle.kts.as32 @@ -1,9 +1,9 @@ -extra["versions.intellijSdk"] = "173.4548.28" +extra["versions.intellijSdk"] = "181.4203.6" extra["versions.androidBuildTools"] = "r23.0.1" -extra["versions.idea.NodeJS"] = "172.3757.32" -extra["versions.androidStudioRelease"] = "3.2.0.2" -extra["versions.androidStudioBuild"] = "173.4595177" +extra["versions.idea.NodeJS"] = "181.3494.12" +//extra["versions.androidStudioRelease"] = "3.1.0.5" +//extra["versions.androidStudioBuild"] = "173.4506631" val gradleJars = listOf( "gradle-api", @@ -31,6 +31,7 @@ when (platform) { extra["versions.jar.kxml2"] = "2.3.0" extra["versions.jar.streamex"] = "0.6.5" extra["versions.jar.gson"] = "2.8.2" + extra["versions.jar.oro"] = "2.0.8" for (jar in gradleJars) { extra["versions.jar.$jar"] = "4.4" }