diff --git a/buildSrc/src/main/kotlin/tasks.kt b/buildSrc/src/main/kotlin/tasks.kt index 0a66ff81df3..8f373894e04 100644 --- a/buildSrc/src/main/kotlin/tasks.kt +++ b/buildSrc/src/main/kotlin/tasks.kt @@ -67,8 +67,8 @@ fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}): dependsOn(":test-instrumenter:jar") - jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1100m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true") - maxHeapSize = "1100m" + jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1600m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true") + maxHeapSize = "1600m" systemProperty("idea.is.unit.test", "true") systemProperty("idea.home.path", intellijRootDir().canonicalPath) environment("NO_FS_ROOTS_ACCESS_CHECK", "true") @@ -120,4 +120,4 @@ fun Task.useAndroidSdk() { fun Task.useAndroidJar() { TaskUtils.useAndroidJar(this) -} \ No newline at end of file +} diff --git a/buildSrc/src/main/kotlin/tasks.kt.173 b/buildSrc/src/main/kotlin/tasks.kt.173 new file mode 100644 index 00000000000..0a66ff81df3 --- /dev/null +++ b/buildSrc/src/main/kotlin/tasks.kt.173 @@ -0,0 +1,123 @@ +/* + * 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. + */ + +@file:Suppress("unused") // usages in build scripts are not tracked properly + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.extra +import org.gradle.kotlin.dsl.task +import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.* +import java.lang.Character.isLowerCase +import java.lang.Character.isUpperCase + +fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}): Test = getOrCreateTask(taskName) { + doFirst { + val patterns = filter.includePatterns + ((filter as? DefaultTestFilter)?.commandLineIncludePatterns ?: emptySet()) + if (patterns.isEmpty() || patterns.any { '*' in it }) return@doFirst + patterns.forEach { pattern -> + val maybeMethodName = pattern.substringAfterLast('.') + val maybeClassFqName = if (maybeMethodName.isFirstChar(::isLowerCase)) + pattern.substringBeforeLast('.') + else + pattern + + if (!maybeClassFqName.substringAfterLast('.').isFirstChar(::isUpperCase)) { + return@forEach + } + + val classFileNameWithoutExtension = maybeClassFqName.replace('.', '/') + val classFileName = classFileNameWithoutExtension + ".class" + + include { + val path = it.path + if (it.isDirectory) { + classFileNameWithoutExtension.startsWith(path) + } else { + path == classFileName || (path.endsWith(".class") && path.startsWith(classFileNameWithoutExtension + "$")) + } + } + } + } + + doFirst { + val agent = tasks.findByPath(":test-instrumenter:jar")!!.outputs.files.singleFile + + val args = project.findProperty("kotlin.test.instrumentation.args")?.let { "=$it" }.orEmpty() + + jvmArgs("-javaagent:$agent$args") + } + + dependsOn(":test-instrumenter:jar") + + jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1100m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true") + maxHeapSize = "1100m" + systemProperty("idea.is.unit.test", "true") + systemProperty("idea.home.path", intellijRootDir().canonicalPath) + environment("NO_FS_ROOTS_ACCESS_CHECK", "true") + environment("PROJECT_CLASSES_DIRS", the().sourceSets.getByName("test").output.classesDirs.asPath) + environment("PROJECT_BUILD_DIR", buildDir) + systemProperty("jps.kotlin.home", rootProject.extra["distKotlinHomeDir"]) + systemProperty("kotlin.ni", if (rootProject.hasProperty("newInferenceTests")) "true" else "false") + body() +} + +private inline fun String.isFirstChar(f: (Char) -> Boolean) = isNotEmpty() && f(first()) + +inline fun Project.getOrCreateTask(taskName: String, body: T.() -> Unit): T = + (tasks.findByName(taskName)?.let { it as T } ?: task(taskName)).apply { body() } + +object TaskUtils { + fun useAndroidSdk(task: Task) { + task.useAndroidConfiguration(systemPropertyName = "android.sdk", configName = "androidSdk") + } + + fun useAndroidJar(task: Task) { + task.useAndroidConfiguration(systemPropertyName = "android.jar", configName = "androidJar") + } +} + +private fun Task.useAndroidConfiguration(systemPropertyName: String, configName: String) { + val configuration = with(project) { + configurations.getOrCreate(configName) + .also { + dependencies.add( + configName, + dependencies.project(":custom-dependencies:android-sdk", configuration = configName) + ) + } + } + + dependsOn(configuration) + + if (this is Test) { + doFirst { + systemProperty(systemPropertyName, configuration.singleFile.canonicalPath) + } + } +} + +fun Task.useAndroidSdk() { + TaskUtils.useAndroidSdk(this) +} + +fun Task.useAndroidJar() { + TaskUtils.useAndroidJar(this) +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index ae40d3dffc5..c63cadb659f 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -395,7 +395,7 @@ class KotlinCoreEnvironment private constructor( } companion object { - private val ideaCompatibleBuildNumber = "173.1" + private val ideaCompatibleBuildNumber = "181.1" init { setCompatibleBuild() diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 new file mode 100644 index 00000000000..ae40d3dffc5 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 @@ -0,0 +1,629 @@ +/* + * 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.codeInsight.ContainerProvider +import com.intellij.codeInsight.ExternalAnnotationsManager +import com.intellij.codeInsight.InferredAnnotationsManager +import com.intellij.codeInsight.runner.JavaMainMethodProvider +import com.intellij.core.CoreApplicationEnvironment +import com.intellij.core.CoreJavaFileManager +import com.intellij.core.JavaCoreApplicationEnvironment +import com.intellij.core.JavaCoreProjectEnvironment +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.lang.MetaLanguage +import com.intellij.lang.java.JavaParserDefinition +import com.intellij.lang.jvm.facade.JvmElementProvider +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.TransactionGuard +import com.intellij.openapi.application.TransactionGuardImpl +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.extensions.ExtensionsArea +import com.intellij.openapi.fileTypes.FileTypeExtensionPoint +import com.intellij.openapi.fileTypes.PlainTextFileType +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.* +import com.intellij.openapi.vfs.impl.ZipHandler +import com.intellij.psi.FileContextProvider +import com.intellij.psi.JavaModuleSystem +import com.intellij.psi.PsiElementFinder +import com.intellij.psi.PsiManager +import com.intellij.psi.augment.PsiAugmentProvider +import com.intellij.psi.augment.TypeAnnotationModifier +import com.intellij.psi.compiled.ClassFileDecompilers +import com.intellij.psi.impl.JavaClassSupersImpl +import com.intellij.psi.impl.PsiElementFinderImpl +import com.intellij.psi.impl.PsiTreeChangePreprocessor +import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy +import com.intellij.psi.impl.file.impl.JavaFileManager +import com.intellij.psi.meta.MetaDataContributor +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.BinaryFileStubBuilders +import com.intellij.psi.util.JavaClassSupers +import com.intellij.util.io.URLUtil +import com.intellij.util.lang.UrlClassLoader +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport +import org.jetbrains.kotlin.asJava.LightClassGenerationSupport +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.finder.JavaElementFinder +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl +import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider +import org.jetbrains.kotlin.cli.common.script.CliScriptDependenciesProvider +import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink +import org.jetbrains.kotlin.cli.common.toBooleanLenient +import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker +import org.jetbrains.kotlin.cli.jvm.config.* +import org.jetbrains.kotlin.cli.jvm.index.* +import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar +import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder +import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver +import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem +import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension +import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension +import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension +import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension +import org.jetbrains.kotlin.extensions.PreprocessedVirtualFileFactoryExtension +import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension +import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache +import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager +import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory +import org.jetbrains.kotlin.parsing.KotlinParserDefinition +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer +import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver +import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension +import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade +import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension +import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension +import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver +import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService +import org.jetbrains.kotlin.script.ScriptDefinitionProvider +import org.jetbrains.kotlin.script.ScriptDependenciesProvider +import org.jetbrains.kotlin.script.ScriptReportSink +import org.jetbrains.kotlin.script.StandardScriptDefinition +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File +import java.util.zip.ZipFile + +class KotlinCoreEnvironment private constructor( + parentDisposable: Disposable, + applicationEnvironment: JavaCoreApplicationEnvironment, + initialConfiguration: CompilerConfiguration, + configFiles: EnvironmentConfigFiles +) { + private val projectEnvironment: JavaCoreProjectEnvironment = object : KotlinCoreProjectEnvironment(parentDisposable, applicationEnvironment) { + override fun preregisterServices() { + registerProjectExtensionPoints(Extensions.getArea(project)) + } + + override fun registerJavaPsiFacade() { + with (project) { + registerService(CoreJavaFileManager::class.java, ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager) + + val traceHolder = CliTraceHolder() + val cliLightClassGenerationSupport = CliLightClassGenerationSupport(traceHolder) + val kotlinAsJavaSupport = CliKotlinAsJavaSupport(this, traceHolder) + registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport) + registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport) + registerService(KotlinAsJavaSupport::class.java, kotlinAsJavaSupport) + registerService(CodeAnalyzerInitializer::class.java, traceHolder) + + registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager()) + registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager()) + + val area = Extensions.getArea(this) + + area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(JavaElementFinder(this, kotlinAsJavaSupport)) + area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension( + PsiElementFinderImpl(this, ServiceManager.getService(this, JavaFileManager::class.java))) + } + + super.registerJavaPsiFacade() + } + } + + private val sourceFiles = mutableListOf() + private val rootsIndex: JvmDependenciesDynamicCompoundIndex + private val packagePartProviders = mutableListOf() + + private val classpathRootsResolver: ClasspathRootsResolver + private val initialRoots: List + + val configuration: CompilerConfiguration = initialConfiguration.apply { setupJdkClasspathRoots(configFiles) }.copy() + + init { + PersistentFSConstants::class.java.getDeclaredField("ourMaxIntellisenseFileSize") + .apply { isAccessible = true } + .setInt(null, FileUtilRt.LARGE_FOR_CONTENT_LOADING) + + val project = projectEnvironment.project + + ExpressionCodegenExtension.registerExtensionPoint(project) + SyntheticResolveExtension.registerExtensionPoint(project) + ClassBuilderInterceptorExtension.registerExtensionPoint(project) + AnalysisHandlerExtension.registerExtensionPoint(project) + PackageFragmentProviderExtension.registerExtensionPoint(project) + StorageComponentContainerContributor.registerExtensionPoint(project) + DeclarationAttributeAltererExtension.registerExtensionPoint(project) + PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project) + JsSyntheticTranslateExtension.registerExtensionPoint(project) + CompilerConfigurationExtension.registerExtensionPoint(project) + + for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) { + try { + registrar.registerProjectComponents(project, configuration) + } catch (e: AbstractMethodError) { + throw IllegalStateException("The provided plugin ${registrar.javaClass.name} is not compatible with this version of compiler", e) + } + } + + project.registerService(DeclarationProviderFactoryService::class.java, CliDeclarationProviderFactoryService(sourceFiles)) + project.registerService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl(configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES)) + + registerProjectServicesForCLI(projectEnvironment) + + val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) + registerProjectServices(projectEnvironment, messageCollector) + + CompilerConfigurationExtension.getInstances(project).forEach { + it.updateConfiguration(configuration) + } + + sourceFiles += CompileEnvironmentUtil.getKtFiles(project, getSourceRootsCheckingForDuplicates(), this.configuration, { + message -> + report(ERROR, message) + }) + sourceFiles.sortBy { it.virtualFile.path } + + // We should always support at least the standard script definition + configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) + + val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider + if (scriptDefinitionProvider != null) { + scriptDefinitionProvider.setScriptDefinitions( + configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS)) + + ScriptDependenciesProvider.getInstance(project).let { importsProvider -> + configuration.addJvmClasspathRoots( + sourceFiles.mapNotNull(importsProvider::getScriptDependencies) + .flatMap { it.classpath } + .distinctBy { it.absolutePath }) + } + } + + val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME) + val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL) + val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path -> + jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR) + }) + + val outputDirectory = + configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory() + ?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath + + classpathRootsResolver = ClasspathRootsResolver( + PsiManager.getInstance(project), + messageCollector, + configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES), + this::contentRootToVirtualFile, + javaModuleFinder, + !configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE), + outputDirectory?.let(this::findLocalFile) + ) + + val (initialRoots, javaModules) = + classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS)) + this.initialRoots = initialRoots + + if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) { + JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency( + messageCollector, + configuration, + initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null } + ) + } + + val (roots, singleJavaFileRoots) = + initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION } + + // REPL and kapt2 update classpath dynamically + rootsIndex = JvmDependenciesDynamicCompoundIndex().apply { + addIndex(JvmDependenciesIndexImpl(roots)) + updateClasspathFromRootsIndex(this) + } + + (ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl).initialize( + rootsIndex, + packagePartProviders, + SingleJavaFileRootsIndex(singleJavaFileRoots), + configuration.getBoolean(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING) + ) + + project.registerService( + JavaModuleResolver::class.java, + CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList()) + ) + + val finderFactory = CliVirtualFileFinderFactory(rootsIndex) + project.registerService(MetadataFinderFactory::class.java, finderFactory) + project.registerService(VirtualFileFinderFactory::class.java, finderFactory) + + project.putUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY, fun(roots: List) { + updateClasspath(roots.map { JavaSourceRoot(it, null) }) + }) + } + + fun createPackagePartProvider(scope: GlobalSearchScope): JvmPackagePartProvider { + return JvmPackagePartProvider(configuration.languageVersionSettings, scope).apply { + addRoots(initialRoots) + packagePartProviders += this + (ModuleAnnotationsResolver.getInstance(project) as CliModuleAnnotationsResolver).addPackagePartProvider(this) + } + } + + private val VirtualFile.javaFiles: List + get() = mutableListOf().apply { + VfsUtilCore.processFilesRecursively(this@javaFiles) { file -> + if (file.fileType == JavaFileType.INSTANCE) { + add(file) + } + true + } + } + + private val allJavaFiles: List + get() = configuration.javaSourceRoots + .mapNotNull(this::findLocalFile) + .flatMap { it.javaFiles } + .map { File(it.canonicalPath) } + + fun registerJavac( + javaFiles: List = allJavaFiles, + kotlinFiles: List = sourceFiles, + arguments: Array? = null, + bootClasspath: List? = null, + sourcePath: List? = null + ): Boolean { + return JavacWrapperRegistrar.registerJavac(projectEnvironment.project, configuration, javaFiles, kotlinFiles, arguments, bootClasspath, sourcePath, LightClassGenerationSupport.getInstance(project)) + } + + private val applicationEnvironment: CoreApplicationEnvironment + get() = projectEnvironment.environment + + val project: Project + get() = projectEnvironment.project + + internal fun countLinesOfCode(sourceFiles: List): Int = + sourceFiles.sumBy { sourceFile -> + val text = sourceFile.text + StringUtil.getLineBreakCount(text) + (if (StringUtil.endsWithLineBreak(text)) 0 else 1) + } + + private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) { + index.indexedRoots.forEach { + projectEnvironment.addSourcesToClasspath(it.file) + } + } + + fun updateClasspath(contentRoots: List): List? { + // TODO: add new Java modules to CliJavaModuleResolver + val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots).roots + + for (packagePartProvider in packagePartProviders) { + packagePartProvider.addRoots(newRoots) + } + + return rootsIndex.addNewIndexForRoots(newRoots)?.let { newIndex -> + updateClasspathFromRootsIndex(newIndex) + newIndex.indexedRoots.mapNotNull { (file) -> + VfsUtilCore.virtualToIoFile(VfsUtilCore.getVirtualFileForJar(file) ?: file) + }.toList() + }.orEmpty() + } + + private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? { + return when (root) { + is JvmClasspathRoot -> if (root.file.isFile) findJarRoot(root.file) else findLocalFile(root) + is JvmModulePathRoot -> if (root.file.isFile) findJarRoot(root.file) else findLocalFile(root) + is JavaSourceRoot -> findLocalFile(root) + else -> throw IllegalStateException("Unexpected root: $root") + } + } + + internal fun findLocalFile(path: String): VirtualFile? = + applicationEnvironment.localFileSystem.findFileByPath(path) + + private fun findLocalFile(root: JvmContentRoot): VirtualFile? { + return findLocalFile(root.file.absolutePath).also { + if (it == null) { + report(STRONG_WARNING, "Classpath entry points to a non-existent location: ${root.file}") + } + } + } + + private fun findJarRoot(file: File): VirtualFile? = + applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}") + + private fun getSourceRootsCheckingForDuplicates(): Collection { + val uniqueSourceRoots = linkedSetOf() + + configuration.kotlinSourceRoots.forEach { path -> + if (!uniqueSourceRoots.add(path)) { + report(STRONG_WARNING, "Duplicate source root: $path") + } + } + + return uniqueSourceRoots + } + + fun getSourceFiles(): List = sourceFiles + + private fun report(severity: CompilerMessageSeverity, message: String) { + configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(severity, message) + } + + companion object { + private val ideaCompatibleBuildNumber = "173.1" + + init { + setCompatibleBuild() + } + + private val APPLICATION_LOCK = Object() + private var ourApplicationEnvironment: JavaCoreApplicationEnvironment? = null + private var ourProjectCount = 0 + + @JvmStatic + fun createForProduction( + parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles + ): KotlinCoreEnvironment { + setCompatibleBuild() + val appEnv = getOrCreateApplicationEnvironmentForProduction(configuration) + // Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally + // breaks a lot of tests, therefore it is disabled for production and enabled for tests + if (System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY).toBooleanLenient() != true) { + // JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ) + // All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well + Disposer.register(parentDisposable, Disposable { + synchronized (APPLICATION_LOCK) { + if (--ourProjectCount <= 0) { + disposeApplicationEnvironment() + } + } + }) + } + val environment = KotlinCoreEnvironment(parentDisposable, appEnv, configuration, configFiles) + + synchronized (APPLICATION_LOCK) { + ourProjectCount++ + } + return environment + } + + @JvmStatic + private fun setCompatibleBuild() { + PluginManagerCore.BUILD_NUMBER = ideaCompatibleBuildNumber + System.getProperties().setProperty("idea.plugins.compatible.build", ideaCompatibleBuildNumber) + } + + @TestOnly + @JvmStatic + fun createForTests( + parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles + ): KotlinCoreEnvironment { + val configuration = initialConfiguration.copy() + if (configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).isEmpty()) { + configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) + } + // Tests are supposed to create a single project and dispose it right after use + return KotlinCoreEnvironment( + parentDisposable, + createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true), + configuration, + extensionConfigs + ) + } + + // used in the daemon for jar cache cleanup + val applicationEnvironment: JavaCoreApplicationEnvironment? get() = ourApplicationEnvironment + + private fun getOrCreateApplicationEnvironmentForProduction(configuration: CompilerConfiguration): JavaCoreApplicationEnvironment { + synchronized (APPLICATION_LOCK) { + if (ourApplicationEnvironment != null) + return ourApplicationEnvironment!! + + val parentDisposable = Disposer.newDisposable() + ourApplicationEnvironment = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = false) + ourProjectCount = 0 + Disposer.register(parentDisposable, Disposable { + synchronized (APPLICATION_LOCK) { + ourApplicationEnvironment = null + } + }) + return ourApplicationEnvironment!! + } + } + + private fun disposeApplicationEnvironment() { + synchronized (APPLICATION_LOCK) { + val environment = ourApplicationEnvironment ?: return + ourApplicationEnvironment = null + Disposer.dispose(environment.parentDisposable) + ZipHandler.clearFileAccessorCache() + } + } + + private fun createApplicationEnvironment( + parentDisposable: Disposable, + configuration: CompilerConfiguration, + unitTestMode: Boolean + ): JavaCoreApplicationEnvironment { + Extensions.cleanRootArea(parentDisposable) + registerAppExtensionPoints() + val applicationEnvironment = object : JavaCoreApplicationEnvironment(parentDisposable, unitTestMode) { + override fun createJrtFileSystem(): VirtualFileSystem? = CoreJrtFileSystem() + } + + registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml") + + registerApplicationServicesForCLI(applicationEnvironment) + registerApplicationServices(applicationEnvironment) + + return applicationEnvironment + } + + private fun registerAppExtensionPoints() { + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), FileContextProvider.EP_NAME, FileContextProvider::class.java) + // + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaDataContributor.EP_NAME, MetaDataContributor::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), PsiAugmentProvider.EP_NAME, PsiAugmentProvider::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider::class.java) + // + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ContainerProvider.EP_NAME, ContainerProvider::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsCustomNavigationPolicy.EP_NAME, ClsCustomNavigationPolicy::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler::class.java) + // + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaLanguage.EP_NAME, MetaLanguage::class.java) + // + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), JavaModuleSystem.EP_NAME, JavaModuleSystem::class.java) + } + + private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) { + fun File.hasConfigFile(configFile: String): Boolean = + if (isDirectory) File(this, "META-INF" + File.separator + configFile).exists() + else try { + ZipFile(this).use { + it.getEntry("META-INF/" + configFile) != null + } + } + catch (e: Throwable) { + false + } + + val pluginRoot = + configuration.get(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT)?.let(::File) + ?: configuration.get(CLIConfigurationKeys.COMPILER_JAR_LOCATOR)?.compilerJar + ?: PathUtil.getResourcePathForClass(this::class.java).takeIf { it.hasConfigFile(configFilePath) } + // hack for load extensions when compiler run directly from project directory (e.g. in tests) + ?: File("idea/src").takeIf { it.hasConfigFile(configFilePath) } + ?: throw IllegalStateException( + "Unable to find extension point configuration $configFilePath " + + "(cp:\n ${(Thread.currentThread().contextClassLoader as? UrlClassLoader)?.urls?.joinToString("\n ") { it.file }})") + + CoreApplicationEnvironment.registerExtensionPointAndExtensions(pluginRoot, configFilePath, Extensions.getRootArea()) + } + + private fun registerApplicationServicesForCLI(applicationEnvironment: JavaCoreApplicationEnvironment) { + // ability to get text from annotations xml files + applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml") + applicationEnvironment.registerParserDefinition(JavaParserDefinition()) + } + + // made public for Upsource + @Suppress("MemberVisibilityCanPrivate") + @JvmStatic + fun registerApplicationServices(applicationEnvironment: JavaCoreApplicationEnvironment) { + with(applicationEnvironment) { + registerFileType(KotlinFileType.INSTANCE, "kt") + registerFileType(KotlinFileType.INSTANCE, KotlinParserDefinition.STD_SCRIPT_SUFFIX) + registerParserDefinition(KotlinParserDefinition()) + application.registerService(KotlinBinaryClassCache::class.java, KotlinBinaryClassCache()) + application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java) + application.registerService(TransactionGuard::class.java, TransactionGuardImpl::class.java) + } + } + + private fun registerProjectExtensionPoints(area: ExtensionsArea) { + CoreApplicationEnvironment.registerExtensionPoint(area, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor::class.java) + CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP_NAME, PsiElementFinder::class.java) + CoreApplicationEnvironment.registerExtensionPoint(area, JvmElementProvider.EP_NAME, JvmElementProvider::class.java) + } + + // made public for Upsource + @JvmStatic + fun registerProjectServices(projectEnvironment: JavaCoreProjectEnvironment, messageCollector: MessageCollector?) { + with (projectEnvironment.project) { + val scriptDefinitionProvider = CliScriptDefinitionProvider() + registerService(ScriptDefinitionProvider::class.java, scriptDefinitionProvider) + registerService(ScriptDependenciesProvider::class.java, CliScriptDependenciesProvider(projectEnvironment.project, scriptDefinitionProvider)) + registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this)) + registerService(KtLightClassForFacade.FacadeStubCache::class.java, KtLightClassForFacade.FacadeStubCache(this)) + registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver()) + if (messageCollector != null) { + registerService(ScriptReportSink::class.java, CliScriptReportSink(messageCollector)) + } + } + } + + private fun registerProjectServicesForCLI(@Suppress("UNUSED_PARAMETER") projectEnvironment: JavaCoreProjectEnvironment) { + /** + * Note that Kapt may restart code analysis process, and CLI services should be aware of that. + * Use PsiManager.getModificationTracker() to ensure that all the data you cached is still valid. + */ + + } + + private fun CompilerConfiguration.setupJdkClasspathRoots(configFiles: EnvironmentConfigFiles) { + if (getBoolean(JVMConfigurationKeys.NO_JDK)) return + + val jvmTarget = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES + if (!jvmTarget) return + + val jdkHome = get(JVMConfigurationKeys.JDK_HOME) + val (javaRoot, classesRoots) = if (jdkHome == null) { + val javaHome = File(System.getProperty("java.home")) + put(JVMConfigurationKeys.JDK_HOME, javaHome) + + javaHome to PathUtil.getJdkClassesRootsFromCurrentJre() + } + else { + jdkHome to PathUtil.getJdkClassesRoots(jdkHome) + } + + if (!CoreJrtFileSystem.isModularJdk(javaRoot)) { + if (classesRoots.isEmpty()) { + val messageCollector = get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) + messageCollector?.report(ERROR, "No class roots are found in the JDK path: $javaRoot") + } + else { + addJvmSdkRoots(classesRoots) + } + } + } + + } +} diff --git a/compiler/daemon/daemon-client/build.gradle.kts b/compiler/daemon/daemon-client/build.gradle.kts index b00b2ee9f89..34bcf979417 100644 --- a/compiler/daemon/daemon-client/build.gradle.kts +++ b/compiler/daemon/daemon-client/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { compileOnly(project(":compiler:daemon-common")) compileOnly(project(":kotlin-reflect-api")) compileOnly(commonDep("net.rubygrapefruit", "native-platform")) + compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) } embeddedComponents(project(":compiler:daemon-common")) { isTransitive = false } embeddedComponents(commonDep("net.rubygrapefruit", "native-platform")) diff --git a/compiler/daemon/daemon-client/build.gradle.kts.173 b/compiler/daemon/daemon-client/build.gradle.kts.173 new file mode 100644 index 00000000000..b00b2ee9f89 --- /dev/null +++ b/compiler/daemon/daemon-client/build.gradle.kts.173 @@ -0,0 +1,47 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +description = "Kotlin Daemon Client" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +jvmTarget = "1.6" + +val nativePlatformVariants: List by rootProject.extra + +dependencies { + compileOnly(project(":compiler:util")) + compileOnly(project(":compiler:cli-common")) + compileOnly(project(":compiler:daemon-common")) + compileOnly(project(":kotlin-reflect-api")) + compileOnly(commonDep("net.rubygrapefruit", "native-platform")) + + embeddedComponents(project(":compiler:daemon-common")) { isTransitive = false } + embeddedComponents(commonDep("net.rubygrapefruit", "native-platform")) + nativePlatformVariants.forEach { + embeddedComponents(commonDep("net.rubygrapefruit", "native-platform", "-$it")) + } +} + +sourceSets { + "main" { projectDefault() } + "test" {} +} + +noDefaultJar() + +runtimeJar(task("shadowJar")) { + from(the().sourceSets.getByName("main").output) + fromEmbeddedComponents() +} + +sourcesJar() +javadocJar() + +dist() + +ideaPlugin() + +publish() diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt index d66b4eef98e..711075fe613 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt @@ -22,9 +22,7 @@ import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.ClassFileViewProvider import com.intellij.psi.PsiElement -import com.intellij.psi.PsiManager import com.intellij.psi.impl.compiled.ClsFileImpl import com.intellij.psi.impl.java.stubs.PsiJavaFileStub import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl @@ -80,10 +78,7 @@ private fun createJavaFileStub(project: Project, packageFqName: FqName, files: C val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /*compiled = */true) javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE - val manager = PsiManager.getInstance(project) - - val virtualFile = getRepresentativeVirtualFile(files) - val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) { + val fakeFile = object : ClsFileImpl(files.first().viewProvider) { override fun getStub() = javaFileStub override fun getPackageName() = packageFqName.asString() @@ -121,10 +116,6 @@ private fun createJavaFileStub(project: Project, packageFqName: FqName, files: C return javaFileStub } -private fun getRepresentativeVirtualFile(files: Collection): VirtualFile { - return files.first().viewProvider.virtualFile -} - private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) { val path = if (virtualFile == null) "" else virtualFile.path LOG.error( diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt.173 b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt.173 new file mode 100644 index 00000000000..d66b4eef98e --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/builder/LightClassBuilder.kt.173 @@ -0,0 +1,137 @@ +/* + * 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.asJava.builder + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.SystemInfo +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.ClassFileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiManager +import com.intellij.psi.impl.compiled.ClsFileImpl +import com.intellij.psi.impl.java.stubs.PsiJavaFileStub +import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl +import com.intellij.psi.impl.source.tree.TreeElement +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import java.lang.StringBuilder + +data class LightClassBuilderResult(val stub: PsiJavaFileStub, val bindingContext: BindingContext, val diagnostics: Diagnostics) + +fun buildLightClass( + packageFqName: FqName, + files: Collection, + generateClassFilter: GenerationState.GenerateClassFilter, + context: LightClassConstructionContext, + generate: (state: GenerationState, files: Collection) -> Unit +): LightClassBuilderResult { + val project = files.first().project + + try { + val classBuilderFactory = KotlinLightClassBuilderFactory(createJavaFileStub(project, packageFqName, files)) + val state = GenerationState.Builder( + project, + classBuilderFactory, + context.module, + context.bindingContext, + files.toList(), + CompilerConfiguration.EMPTY + ).generateDeclaredClassFilter(generateClassFilter).wantsDiagnostics(false).build() + state.beforeCompile() + + generate(state, files) + + val javaFileStub = classBuilderFactory.result() + + ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub, context) + return LightClassBuilderResult(javaFileStub, context.bindingContext, state.collectedExtraJvmDiagnostics) + } + catch (e: ProcessCanceledException) { + throw e + } + catch (e: RuntimeException) { + logErrorWithOSInfo(e, packageFqName, null) + throw e + } +} + +private fun createJavaFileStub(project: Project, packageFqName: FqName, files: Collection): PsiJavaFileStub { + val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /*compiled = */true) + javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE + + val manager = PsiManager.getInstance(project) + + val virtualFile = getRepresentativeVirtualFile(files) + val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) { + override fun getStub() = javaFileStub + + override fun getPackageName() = packageFqName.asString() + + override fun isPhysical() = false + + override fun appendMirrorText(indentLevel: Int, buffer: StringBuilder) { + if (files.size == 1) { + LOG.error("Mirror text should never be calculated for light classes generated from a single file") + } + super.appendMirrorText(indentLevel, buffer) + } + + + override fun setMirror(element: TreeElement) { + if (files.size == 1) { + LOG.error("Mirror element should never be set for light classes generated from a single file") + } + super.setMirror(element) + } + + override fun getMirror(): PsiElement { + if (files.size == 1) { + LOG.error("Mirror element should never be calculated for light classes generated from a single file") + } + return super.getMirror() + } + + override fun getText(): String { + return files.singleOrNull()?.text ?: super.getText() + } + } + + javaFileStub.psi = fakeFile + return javaFileStub +} + +private fun getRepresentativeVirtualFile(files: Collection): VirtualFile { + return files.first().viewProvider.virtualFile +} + +private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) { + val path = if (virtualFile == null) "" else virtualFile.path + LOG.error( + "Could not generate LightClass for $fqName declared in $path\n" + + "System: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION} Java Runtime: ${SystemInfo.JAVA_RUNTIME_VERSION}", + cause + ) +} + +private val LOG = Logger.getInstance(LightClassBuilderResult::class.java) \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt index 8645df9afd3..158f9ae7a82 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt @@ -16,8 +16,8 @@ package org.jetbrains.kotlin.asJava.elements +import com.intellij.openapi.vfs.VirtualFile import com.intellij.pom.java.LanguageLevel -import com.intellij.psi.ClassFileViewProvider import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.impl.compiled.ClsFileImpl @@ -33,9 +33,11 @@ open class FakeFileForLightClass( private val lightClass: () -> KtLightClass, private val stub: () -> PsiClassHolderFileStub<*>, private val packageFqName: FqName = ktFile.packageFqName -) : ClsFileImpl(ClassFileViewProvider(ktFile.manager, ktFile.virtualFile ?: - ktFile.originalFile.virtualFile ?: - ktFile.viewProvider.virtualFile)) { +) : ClsFileImpl(ktFile.viewProvider) { + + override fun getVirtualFile(): VirtualFile = + ktFile.virtualFile ?: ktFile.originalFile.virtualFile ?: super.getVirtualFile() + override fun getPackageName() = packageFqName.asString() override fun getStub() = stub() @@ -83,4 +85,4 @@ open class FakeFileForLightClass( } override fun isPhysical() = false -} \ No newline at end of file +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt.173 b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt.173 new file mode 100644 index 00000000000..8645df9afd3 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/FakeFileForLightClass.kt.173 @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2016 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.asJava.elements + +import com.intellij.pom.java.LanguageLevel +import com.intellij.psi.ClassFileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.impl.compiled.ClsFileImpl +import com.intellij.psi.stubs.PsiClassHolderFileStub +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile + +open class FakeFileForLightClass( + val ktFile: KtFile, + private val lightClass: () -> KtLightClass, + private val stub: () -> PsiClassHolderFileStub<*>, + private val packageFqName: FqName = ktFile.packageFqName +) : ClsFileImpl(ClassFileViewProvider(ktFile.manager, ktFile.virtualFile ?: + ktFile.originalFile.virtualFile ?: + ktFile.viewProvider.virtualFile)) { + override fun getPackageName() = packageFqName.asString() + + override fun getStub() = stub() + + override fun getClasses() = arrayOf(lightClass()) + + override fun getNavigationElement() = ktFile + + override fun accept(visitor: PsiElementVisitor) { + // Prevent access to compiled PSI + // TODO: More complex traversal logic may be implemented when necessary + } + + // this should be equal to current compiler target language level + override fun getLanguageLevel() = LanguageLevel.JDK_1_6 + + override fun hashCode(): Int { + val thisClass = lightClass() + if (thisClass is KtLightClassForSourceDeclaration) return ktFile.hashCode() + return thisClass.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is FakeFileForLightClass) return false + val thisClass = lightClass() + val anotherClass = other.lightClass() + + if (thisClass is KtLightClassForSourceDeclaration) { + return anotherClass is KtLightClassForSourceDeclaration && ktFile == other.ktFile + } + + return thisClass == anotherClass + } + + override fun isEquivalentTo(another: PsiElement?) = this == another + + override fun setPackageName(packageName: String) { + if (lightClass() is KtLightClassForFacade) { + ktFile.packageDirective?.fqName = FqName(packageName) + } + else { + super.setPackageName(packageName) + } + } + + override fun isPhysical() = false +} \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt index 9bb99f0f0db..16b490a185c 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt @@ -95,12 +95,13 @@ class KtLightAnnotationForSourceEntry( override fun getReferences() = originalExpression?.references.orEmpty() override fun getLanguage() = KotlinLanguage.INSTANCE override fun getNavigationElement() = originalExpression - override fun isPhysical(): Boolean = originalExpression?.containingFile == kotlinOrigin.containingFile + override fun isPhysical(): Boolean = false override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0 override fun getParent() = parent override fun getText() = originalExpression?.text.orEmpty() - override fun getContainingFile(): PsiFile? = if (isPhysical) kotlinOrigin.containingFile else delegate.containingFile + override fun getContainingFile(): PsiFile? = if (originalExpression?.containingFile == kotlinOrigin.containingFile) + kotlinOrigin.containingFile else delegate.containingFile override fun replace(newElement: PsiElement): PsiElement { val value = (newElement as? PsiLiteral)?.value as? String ?: return this @@ -250,8 +251,6 @@ class KtLightAnnotationForSourceEntry( else -> LightElementValue(value, parent, ktOrigin) } - override fun isPhysical() = true - override fun getName() = null private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 new file mode 100644 index 00000000000..9bb99f0f0db --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/elements/lightAnnotations.kt.173 @@ -0,0 +1,380 @@ +/* + * Copyright 2010-2016 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.asJava.elements + +import com.intellij.openapi.diagnostic.Attachment +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.TextRange +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.* +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable +import org.jetbrains.kotlin.asJava.LightClassGenerationSupport +import org.jetbrains.kotlin.asJava.classes.cannotModify +import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.TypeUtils + +private val LOG = Logger.getInstance("#org.jetbrains.kotlin.asJava.elements.lightAnnotations") + +abstract class KtLightAbstractAnnotation(parent: PsiElement, computeDelegate: () -> PsiAnnotation) : + KtLightElementBase(parent), PsiAnnotation, KtLightElement { + override val clsDelegate by lazyPub(computeDelegate) + + override fun getNameReferenceElement() = clsDelegate.nameReferenceElement + + override fun getOwner() = parent as? PsiAnnotationOwner + + override fun getMetaData() = clsDelegate.metaData + + override fun getParameterList() = clsDelegate.parameterList + + override fun canNavigate(): Boolean = super.canNavigate() + + override fun canNavigateToSource(): Boolean = super.canNavigateToSource() + + override fun navigate(requestFocus: Boolean) = super.navigate(requestFocus) + + open fun fqNameMatches(fqName: String): Boolean = qualifiedName == fqName +} + +private typealias AnnotationValueOrigin = () -> PsiElement? + +class KtLightAnnotationForSourceEntry( + private val qualifiedName: String, + override val kotlinOrigin: KtCallElement, + parent: PsiElement, + computeDelegate: () -> PsiAnnotation +) : KtLightAbstractAnnotation(parent, computeDelegate) { + + override fun getQualifiedName() = qualifiedName + + open inner class LightElementValue( + val delegate: D, + private val parent: PsiElement, + valueOrigin: AnnotationValueOrigin + ) : PsiAnnotationMemberValue, PsiCompiledElement, PsiElement by delegate { + override fun getMirror(): PsiElement = delegate + + val originalExpression: PsiElement? by lazyPub(valueOrigin) + + fun getConstantValue(): Any? { + val expression = originalExpression as? KtExpression ?: return null + val annotationEntry = this@KtLightAnnotationForSourceEntry.kotlinOrigin + val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry) + return context[BindingContext.COMPILE_TIME_VALUE, expression]?.getValue(TypeUtils.NO_EXPECTED_TYPE) + } + + override fun getReference() = references.singleOrNull() + override fun getReferences() = originalExpression?.references.orEmpty() + override fun getLanguage() = KotlinLanguage.INSTANCE + override fun getNavigationElement() = originalExpression + override fun isPhysical(): Boolean = originalExpression?.containingFile == kotlinOrigin.containingFile + override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE + override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0 + override fun getParent() = parent + override fun getText() = originalExpression?.text.orEmpty() + override fun getContainingFile(): PsiFile? = if (isPhysical) kotlinOrigin.containingFile else delegate.containingFile + + override fun replace(newElement: PsiElement): PsiElement { + val value = (newElement as? PsiLiteral)?.value as? String ?: return this + val origin = originalExpression + + val exprToReplace = + if (origin is KtCallExpression /*arrayOf*/) { + unwrapArray(origin.valueArguments) + } + else { + origin as? KtExpression + } ?: return this + exprToReplace.replace(KtPsiFactory(this).createExpression("\"${StringUtil.escapeStringCharacters(value)}\"")) + + return this + } + } + + private fun getMemberValueAsCallArgument(memberValue: PsiElement, callHolder: KtCallElement): PsiElement? { + val resolvedCall = callHolder.getResolvedCall() ?: return null + val annotationConstructor = resolvedCall.resultingDescriptor + val parameterName = + memberValue.getNonStrictParentOfType()?.name ?: + memberValue.getNonStrictParentOfType()?.takeIf { it.containingClass?.isAnnotationType == true }?.name ?: + "value" + + val parameter = annotationConstructor.valueParameters.singleOrNull { it.name.asString() == parameterName } ?: return null + val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return null + return when (resolvedArgument) { + is DefaultValueArgument -> { + val psi = parameter.source.getPsi() + when (psi) { + is KtParameter -> psi.defaultValue + is PsiAnnotationMethod -> psi.defaultValue + else -> error("$psi of type ${psi?.javaClass}") + } + } + + is ExpressionValueArgument -> { + val argExpression = resolvedArgument.valueArgument?.getArgumentExpression() + argExpression?.asKtCall() + ?: argExpression + ?: error("resolvedArgument ($resolvedArgument) has no arg expression") + } + + is VarargValueArgument -> + memberValue.unwrapArray(resolvedArgument.arguments) + ?: resolvedArgument.arguments.first().asElement().let { + (it as? KtValueArgument) + ?.takeIf { + it.getSpreadElement() != null || + it.getArgumentName() != null || + it.getArgumentExpression() is KtCollectionLiteralExpression + } + ?.getArgumentExpression() ?: it.parent + } + + else -> error("resolvedArgument: ${resolvedArgument.javaClass} cant be processed") + } + } + + private fun PsiElement.unwrapArray(arguments: List): PsiElement? { + val arrayInitializer = parent as? PsiArrayInitializerMemberValue ?: return null + val exprIndex = arrayInitializer.initializers.indexOf(this) + if (exprIndex < 0 || exprIndex >= arguments.size) return null + return arguments[exprIndex].getArgumentExpression() + } + + open inner class LightExpressionValue( + delegate: D, + parent: PsiElement, + valueOrigin: AnnotationValueOrigin + ) : LightElementValue(delegate, parent, valueOrigin), PsiExpression { + override fun getType(): PsiType? = delegate.type + } + + inner class LightPsiLiteral( + delegate: PsiLiteralExpression, + parent: PsiElement, + valueOrigin: AnnotationValueOrigin + ) : LightExpressionValue(delegate, parent, valueOrigin), PsiLiteralExpression { + override fun getValue() = delegate.value + } + + inner class LightClassLiteral( + delegate: PsiClassObjectAccessExpression, + parent: PsiElement, + valueOrigin: AnnotationValueOrigin + ) : LightExpressionValue(delegate, parent, valueOrigin), PsiClassObjectAccessExpression { + override fun getType() = delegate.type + override fun getOperand(): PsiTypeElement = delegate.operand + } + + inner class LightArrayInitializerValue( + delegate: PsiArrayInitializerMemberValue, + parent: PsiElement, + valueOrigin: AnnotationValueOrigin + ) : LightElementValue(delegate, parent, valueOrigin), PsiArrayInitializerMemberValue { + private val _initializers by lazyPub { + delegate.initializers.mapIndexed { i, memberValue -> + wrapAnnotationValue(memberValue, this, { + originalExpression.let { ktOrigin -> + when { + ktOrigin is KtCallExpression + && memberValue is PsiAnnotation + && isAnnotationConstructorCall(ktOrigin, memberValue) -> ktOrigin + ktOrigin is KtValueArgumentList -> ktOrigin.arguments.getOrNull(i)?.getArgumentExpression() + ktOrigin is KtCallElement -> ktOrigin.valueArguments.getOrNull(i)?.getArgumentExpression() + ktOrigin is KtCollectionLiteralExpression -> ktOrigin.getInnerExpressions().getOrNull(i) + delegate.initializers.size == 1 -> ktOrigin + else -> null + }.also { + if (it == null) + LOG.error("error wrapping ${memberValue.javaClass} for ${ktOrigin?.javaClass} in ${ktOrigin?.containingFile}", + Attachment( + "source_fragments.txt", + "origin: '${psiReport(ktOrigin)}', delegate: ${psiReport(delegate)}, parent: ${psiReport(parent)}" + ) + ) + } + } + }) + }.toTypedArray() + } + + override fun getInitializers() = _initializers + } + + private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement, ktOrigin: AnnotationValueOrigin): PsiAnnotationMemberValue = + when { + value is PsiLiteralExpression -> LightPsiLiteral(value, parent, ktOrigin) + value is PsiClassObjectAccessExpression -> LightClassLiteral(value, parent, ktOrigin) + value is PsiExpression -> LightExpressionValue(value, parent, ktOrigin) + value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value, parent, ktOrigin) + value is PsiAnnotation -> { + val origin = ktOrigin() + val ktCallElement = origin?.asKtCall() + val qualifiedName = value.qualifiedName + if (qualifiedName != null && ktCallElement != null) + KtLightAnnotationForSourceEntry(qualifiedName, ktCallElement, parent, { value }) + else { + LOG.error("can't convert ${origin?.javaClass} to KtCallElement in ${origin?.containingFile} (value = ${value.javaClass})", + Attachment("source_fragments.txt", "origin: '${psiReport(origin)}', value: '${psiReport(value)}'")) + LightElementValue(value, parent, ktOrigin) // or maybe create a LightErrorAnnotationMemberValue instead? + } + } + else -> LightElementValue(value, parent, ktOrigin) + } + + override fun isPhysical() = true + + override fun getName() = null + + private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, { + getMemberValueAsCallArgument(value, kotlinOrigin) + }) + + override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it) } + + override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it) } + + override fun getParameterList(): PsiAnnotationParameterList = KtLightAnnotationParameterList(super.getParameterList()) + + inner class KtLightAnnotationParameterList(private val list: PsiAnnotationParameterList) : KtLightElementBase(this), + PsiAnnotationParameterList { + override val kotlinOrigin get() = null + override fun getAttributes(): Array = list.attributes.map { KtLightPsiNameValuePair(it) }.toTypedArray() + + inner class KtLightPsiNameValuePair(private val psiNameValuePair: PsiNameValuePair) : KtLightElementBase(this), + PsiNameValuePair { + override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue = psiNameValuePair.setValue(newValue) + override fun getNameIdentifier(): PsiIdentifier? = psiNameValuePair.nameIdentifier + override fun getLiteralValue(): String? = (value as? PsiLiteralValue)?.value?.toString() + override val kotlinOrigin: KtElement? = null + + override fun getValue(): PsiAnnotationMemberValue? = psiNameValuePair.value?.let { wrapAnnotationValue(it) } + } + } + + + override fun delete() = kotlinOrigin.delete() + + override fun toString() = "@$qualifiedName" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class.java != this::class.java) return false + return kotlinOrigin == (other as KtLightAnnotationForSourceEntry).kotlinOrigin + } + + override fun hashCode() = kotlinOrigin.hashCode() + + override fun setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify() +} + +class KtLightNonSourceAnnotation( + parent: PsiElement, clsDelegate: PsiAnnotation +) : KtLightAbstractAnnotation(parent, { clsDelegate }) { + override val kotlinOrigin: KtAnnotationEntry? get() = null + override fun getQualifiedName() = clsDelegate.qualifiedName + override fun setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify() + override fun findAttributeValue(attributeName: String?) = clsDelegate.findAttributeValue(attributeName) + override fun findDeclaredAttributeValue(attributeName: String?) = clsDelegate.findDeclaredAttributeValue(attributeName) +} + +class KtLightNonExistentAnnotation(parent: KtLightElement<*, *>) : KtLightElementBase(parent), PsiAnnotation { + override val kotlinOrigin get() = null + override fun toString() = this.javaClass.name + + override fun setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify() + + override fun getNameReferenceElement() = null + override fun findAttributeValue(attributeName: String?) = null + override fun getQualifiedName() = null + override fun getOwner() = parent as? PsiAnnotationOwner + override fun findDeclaredAttributeValue(attributeName: String?) = null + override fun getMetaData() = null + override fun getParameterList() = KtLightEmptyAnnotationParameterList(this) + + override fun canNavigate(): Boolean = super.canNavigate() + + override fun canNavigateToSource(): Boolean = super.canNavigateToSource() + + override fun navigate(requestFocus: Boolean) = super.navigate(requestFocus) +} + +class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBase(parent), PsiAnnotationParameterList { + override val kotlinOrigin get() = null + override fun getAttributes(): Array = emptyArray() +} + +class KtLightNullabilityAnnotation(member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : KtLightAbstractAnnotation(parent, { + // searching for last because nullability annotations are generated after backend generates source annotations + member.clsDelegate.modifierList?.annotations?.findLast { + isNullabilityAnnotation(it.qualifiedName) + } ?: KtLightNonExistentAnnotation(member) +}) { + override fun fqNameMatches(fqName: String): Boolean { + if (!isNullabilityAnnotation(fqName)) return false + + return super.fqNameMatches(fqName) + } + + override val kotlinOrigin get() = null + override fun setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify() + + override fun findAttributeValue(attributeName: String?) = null + + override fun getQualifiedName(): String? = clsDelegate.qualifiedName + + override fun findDeclaredAttributeValue(attributeName: String?) = null +} + +internal fun isNullabilityAnnotation(qualifiedName: String?) = qualifiedName in backendNullabilityAnnotations + +private val backendNullabilityAnnotations = arrayOf(Nullable::class.java.name, NotNull::class.java.name) + +private fun KtElement.getResolvedCall(): ResolvedCall? { + val context = LightClassGenerationSupport.getInstance(this.project).analyze(this) + return this.getResolvedCall(context) +} + +private fun isAnnotationConstructorCall(callExpression: KtCallExpression, psiAnnotation: PsiAnnotation) = + (callExpression.getResolvedCall()?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass?.fqNameUnsafe?.toString() == psiAnnotation.qualifiedName + +private fun PsiElement.asKtCall(): KtCallElement? = (this as? KtElement)?.getResolvedCall()?.call?.callElement as? KtCallElement + +private fun psiReport(psiElement: PsiElement?): String { + if (psiElement == null) return "null" + val text = try { + psiElement.text + } + catch (e: Exception) { + "${e.javaClass.simpleName}:${e.message}" + } + return "${psiElement.javaClass.canonicalName}[$text]" +} \ No newline at end of file diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt index 359e1d2b943..cac16b5323f 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt @@ -137,3 +137,6 @@ fun KtClassOrObject.getOrCreateBody(): KtClassBody { if (this is KtEnumEntry) return addAfter(newBody, initializerList ?: nameIdentifier) as KtClassBody return add(newBody) as KtClassBody } + +val KtClassOrObject.allConstructors + get() = listOfNotNull(primaryConstructor) + secondaryConstructors \ No newline at end of file diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt.173 b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt.173 new file mode 100644 index 00000000000..359e1d2b943 --- /dev/null +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt.173 @@ -0,0 +1,139 @@ +/* + * 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.psi + +import com.intellij.lang.ASTNode +import com.intellij.navigation.ItemPresentation +import com.intellij.navigation.ItemPresentationProviders +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.CheckUtil +import com.intellij.psi.stubs.IStubElementType +import com.intellij.psi.tree.TokenSet +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes + +abstract class KtClassOrObject : + KtTypeParameterListOwnerStub>, KtDeclarationContainer, KtNamedDeclaration, + KtPureClassOrObject { + constructor(node: ASTNode) : super(node) + constructor(stub: KotlinClassOrObjectStub, nodeType: IStubElementType<*, *>) : super(stub, nodeType) + + fun getSuperTypeList(): KtSuperTypeList? = getStubOrPsiChild(KtStubElementTypes.SUPER_TYPE_LIST) + + override fun getSuperTypeListEntries(): List = getSuperTypeList()?.entries.orEmpty() + + fun addSuperTypeListEntry(superTypeListEntry: KtSuperTypeListEntry): KtSuperTypeListEntry { + getSuperTypeList()?.let { + val single = it.entries.singleOrNull() + if (single != null && single.typeReference?.typeElement == null) { + return single.replace(superTypeListEntry) as KtSuperTypeListEntry + } + return EditCommaSeparatedListHelper.addItem(it, superTypeListEntries, superTypeListEntry) + } + + val psiFactory = KtPsiFactory(this) + val specifierListToAdd = psiFactory.createSuperTypeCallEntry("A()").replace(superTypeListEntry).parent + val colon = addBefore(psiFactory.createColon(), getBody()) + return (addAfter(specifierListToAdd, colon) as KtSuperTypeList).entries.first() + } + + fun removeSuperTypeListEntry(superTypeListEntry: KtSuperTypeListEntry) { + val specifierList = getSuperTypeList() ?: return + assert(superTypeListEntry.parent === specifierList) + + if (specifierList.entries.size > 1) { + EditCommaSeparatedListHelper.removeItem(superTypeListEntry) + } else { + deleteChildRange(findChildByType(KtTokens.COLON) ?: specifierList, specifierList) + } + } + + fun getAnonymousInitializers(): List = getBody()?.anonymousInitializers.orEmpty() + + fun getBody(): KtClassBody? = getStubOrPsiChild(KtStubElementTypes.CLASS_BODY) + + inline fun addDeclaration(declaration: T): T { + val body = getOrCreateBody() + val anchor = PsiTreeUtil.skipSiblingsBackward(body.rBrace ?: body.lastChild!!, PsiWhiteSpace::class.java) + return body.addAfter(declaration, anchor) as T + } + + inline fun addDeclarationAfter(declaration: T, anchor: PsiElement?): T { + val anchorBefore = anchor ?: declarations.lastOrNull() ?: return addDeclaration(declaration) + return getOrCreateBody().addAfter(declaration, anchorBefore) as T + } + + inline fun addDeclarationBefore(declaration: T, anchor: PsiElement?): T { + val anchorAfter = anchor ?: declarations.firstOrNull() ?: return addDeclaration(declaration) + return getOrCreateBody().addBefore(declaration, anchorAfter) as T + } + + fun isTopLevel(): Boolean = stub?.isTopLevel() ?: (parent is KtFile) + + override fun isLocal(): Boolean = stub?.isLocal() ?: KtPsiUtil.isLocal(this) + + override fun getDeclarations(): List = getBody()?.declarations.orEmpty() + + override fun getPresentation(): ItemPresentation? = ItemPresentationProviders.getItemPresentation(this) + + override fun getPrimaryConstructor(): KtPrimaryConstructor? = getStubOrPsiChild(KtStubElementTypes.PRIMARY_CONSTRUCTOR) + + override fun getPrimaryConstructorModifierList(): KtModifierList? = primaryConstructor?.modifierList + + fun getPrimaryConstructorParameterList(): KtParameterList? = primaryConstructor?.valueParameterList + + override fun getPrimaryConstructorParameters(): List = getPrimaryConstructorParameterList()?.parameters.orEmpty() + + override fun hasExplicitPrimaryConstructor(): Boolean = primaryConstructor != null + + override fun hasPrimaryConstructor(): Boolean = hasExplicitPrimaryConstructor() || !hasSecondaryConstructors() + + private fun hasSecondaryConstructors(): Boolean = !secondaryConstructors.isEmpty() + + override fun getSecondaryConstructors(): List = getBody()?.secondaryConstructors.orEmpty() + + fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD) + + fun getDeclarationKeyword(): PsiElement? = + findChildByType( + TokenSet.create( + KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD, KtTokens.OBJECT_KEYWORD + ) + ) + + override fun delete() { + CheckUtil.checkWritable(this) + + val file = containingKtFile + if (!isTopLevel() || file.declarations.size > 1) { + super.delete() + } else { + file.delete() + } + } +} + +fun KtClassOrObject.getOrCreateBody(): KtClassBody { + getBody()?.let { return it } + + val newBody = KtPsiFactory(this).createEmptyClassBody() + if (this is KtEnumEntry) return addAfter(newBody, initializerList ?: nameIdentifier) as KtClassBody + return add(newBody) as KtClassBody +} diff --git a/compiler/tests-common/build.gradle.kts b/compiler/tests-common/build.gradle.kts index 6fb1115857d..f42092dd065 100644 --- a/compiler/tests-common/build.gradle.kts +++ b/compiler/tests-common/build.gradle.kts @@ -31,7 +31,7 @@ dependencies { testCompile(androidDxJar()) { isTransitive = false } testCompile(intellijCoreDep()) { includeJars("intellij-core"); isTransitive = false } testCompile(intellijDep()) { - includeJars("openapi", "idea", "idea_rt", "guava", "trove4j", "picocontainer", "asm-all", "log4j", "jdom", "annotations", rootProject = rootProject) + includeJars("openapi", "platform-api", "platform-impl", "idea", "idea_rt", "guava", "trove4j", "picocontainer", "asm-all", "log4j", "jdom", "bootstrap", "annotations", rootProject = rootProject) isTransitive = false } } diff --git a/compiler/tests-common/build.gradle.kts.173 b/compiler/tests-common/build.gradle.kts.173 new file mode 100644 index 00000000000..6fb1115857d --- /dev/null +++ b/compiler/tests-common/build.gradle.kts.173 @@ -0,0 +1,44 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testCompile(project(":core:descriptors")) + testCompile(project(":core:descriptors.jvm")) + testCompile(project(":core:deserialization")) + testCompile(project(":compiler:util")) + testCompile(project(":compiler:backend")) + testCompile(project(":compiler:ir.ir2cfg")) + testCompile(project(":compiler:frontend")) + testCompile(project(":compiler:frontend.java")) + testCompile(project(":compiler:util")) + testCompile(project(":compiler:cli-common")) + testCompile(project(":compiler:cli")) + testCompile(project(":compiler:light-classes")) + testCompile(project(":compiler:serialization")) + testCompile(project(":kotlin-preloader")) + testCompile(project(":compiler:daemon-common")) + testCompile(project(":js:js.serializer")) + testCompile(project(":js:js.frontend")) + testCompile(project(":js:js.translator")) + testCompileOnly(project(":plugins:android-extensions-compiler")) + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(projectTests(":compiler:tests-common-jvm6")) + testCompileOnly(project(":kotlin-reflect-api")) + testCompile(commonDep("junit:junit")) + testCompile(androidDxJar()) { isTransitive = false } + testCompile(intellijCoreDep()) { includeJars("intellij-core"); isTransitive = false } + testCompile(intellijDep()) { + includeJars("openapi", "idea", "idea_rt", "guava", "trove4j", "picocontainer", "asm-all", "log4j", "jdom", "annotations", rootProject = rootProject) + isTransitive = false + } +} + +sourceSets { + "main" { } + "test" { projectDefault() } +} + +testsJar {} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java index 2e95577877a..a8ca78061f5 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java @@ -429,11 +429,11 @@ public class KotlinTestUtils { private static void deleteOnShutdown(File file) { if (filesToDelete.isEmpty()) { - ShutDownTracker.getInstance().registerShutdownTask(() -> ShutDownTracker.invokeAndWait(true, true, () -> { + ShutDownTracker.getInstance().registerShutdownTask(() -> { for (File victim : filesToDelete) { FileUtil.delete(victim); } - })); + }); } filesToDelete.add(file); diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java.173 b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java.173 new file mode 100644 index 00000000000..2e95577877a --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java.173 @@ -0,0 +1,1282 @@ +/* + * 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.PathUtil; +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.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.*; + +public class KotlinTestUtils { + public static String TEST_MODULE_NAME = "test-module"; + + public static final String TEST_GENERATOR_NAME = "org.jetbrains.kotlin.generators.tests.TestsPackage"; + private static final String PLEASE_REGENERATE_TESTS = "Please regenerate tests (GenerateTests.kt)"; + + private static final boolean RUN_IGNORED_TESTS_AS_REGULAR = + Boolean.getBoolean("org.jetbrains.kotlin.run.ignored.tests.as.regular"); + + private static final boolean AUTOMATICALLY_UNMUTE_PASSED_TESTS = true; + private static final boolean AUTOMATICALLY_MUTE_FAILED_TESTS = false; + + 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 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(() -> ShutDownTracker.invokeAndWait(true, true, () -> { + 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 if (SystemInfo.IS_AT_LEAST_JAVA9) { + configuration.put(JVMConfigurationKeys.JDK_HOME, new File(System.getProperty("java.home"))); + } + + if (configurationKind.getWithCoroutines()) { + JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.coroutinesJarForTests()); + } + 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(@Nullable String testFileName, String expectedText, TestFileFactory factory, String coroutinesPackage) { + return createTestFiles(testFileName, expectedText, factory, false, coroutinesPackage); + } + + @NotNull + public static List createTestFiles(String testFileName, String expectedText, TestFileFactory factory, + boolean preserveLocations, String coroutinesPackage) { + 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; + if (coroutinesPackage.isEmpty()) { + coroutinesPackage = "kotlin.coroutines.experimental"; + } + testFiles.add(factory.createFile(supportModule, + "CoroutineUtil.kt", + "package helpers\n" + + "import " + coroutinesPackage + ".*\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 interface DoTest { + void invoke(String filePath) throws Exception; + } + + // In this test runner version the `testDataFile` parameter is annotated by `TestDataFile`. + // So only file paths passed to this parameter will be used in navigation actions, like "Navigate to testdata" and "Related Symbol..." + public static void runTest(DoTest test, TargetBackend targetBackend, @TestDataFile String testDataFile) throws Exception { + runTest0(test, targetBackend, testDataFile); + } + + // In this test runner version, NONE of the parameters are annotated by `TestDataFile`. + // So DevKit will use test name to determine related files in navigation actions, like "Navigate to testdata" and "Related Symbol..." + // + // Pro: + // * in most cases, it shows all related files including generated js files, for example. + // Cons: + // * sometimes, for too common/general names, it shows many variants to navigate + // * it adds an additional step for navigation -- you must choose an exact file to navigate + public static void runTest0(DoTest test, TargetBackend targetBackend, String testDataFilePath) throws Exception { + File testDataFile = new File(testDataFilePath); + + boolean isIgnored = isIgnoredTarget(targetBackend, testDataFile); + + try { + test.invoke(testDataFilePath); + } + catch (Throwable e) { + + if (!isIgnored && AUTOMATICALLY_MUTE_FAILED_TESTS) { + String text = doLoadFile(testDataFile); + String directive = InTextDirectivesUtils.IGNORE_BACKEND_DIRECTIVE_PREFIX + targetBackend.name(); + String newText = directive + "\n" + text; + + if (!newText.equals(text)) { + System.err.println("\"" + directive + "\" was added to \"" + testDataFile + "\""); + FileUtil.writeToFile(testDataFile, newText); + } + } + + if (RUN_IGNORED_TESTS_AS_REGULAR || !isIgnored) { + throw e; + } + + e.printStackTrace(); + return; + } + + if (isIgnored) { + if (AUTOMATICALLY_UNMUTE_PASSED_TESTS) { + String text = doLoadFile(testDataFile); + String directive = InTextDirectivesUtils.IGNORE_BACKEND_DIRECTIVE_PREFIX + targetBackend.name(); + String newText = Pattern.compile("^" + directive + "\n", Pattern.MULTILINE).matcher(text).replaceAll(""); + + if (!newText.equals(text)) { + System.err.println("\"" + directive + "\" was removed from \"" + testDataFile + "\""); + FileUtil.writeToFile(testDataFile, newText); + } + } + + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive."); + } + } + + 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/idea/build.gradle.kts b/idea/build.gradle.kts index 616a46a00cd..faaa648c8c1 100644 --- a/idea/build.gradle.kts +++ b/idea/build.gradle.kts @@ -95,6 +95,7 @@ dependencies { testRuntime(intellijPluginDep("coverage")) testRuntime(intellijPluginDep("maven")) testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) testRuntime(intellijPluginDep("testng")) } diff --git a/idea/build.gradle.kts.173 b/idea/build.gradle.kts.173 new file mode 100644 index 00000000000..616a46a00cd --- /dev/null +++ b/idea/build.gradle.kts.173 @@ -0,0 +1,126 @@ +import org.gradle.jvm.tasks.Jar + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testRuntime(intellijDep()) + + compile(projectDist(":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("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-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt index 604394ebf75..d2eab310c75 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt @@ -35,7 +35,7 @@ import com.intellij.util.xmlb.XmlSerializerUtil return this } - override fun loadState(state: KotlinCompilerWorkspaceSettings?) { - XmlSerializerUtil.copyBean(state!!, this) + override fun loadState(state: KotlinCompilerWorkspaceSettings) { + XmlSerializerUtil.copyBean(state, this) } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt.173 b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt.173 new file mode 100644 index 00000000000..604394ebf75 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerWorkspaceSettings.kt.173 @@ -0,0 +1,41 @@ +/* + * 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.compiler.configuration + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.StoragePathMacros +import com.intellij.util.xmlb.XmlSerializerUtil + +@State( + name = "KotlinCompilerWorkspaceSettings", + storages = arrayOf( + Storage(file = StoragePathMacros.WORKSPACE_FILE) + ) +) class KotlinCompilerWorkspaceSettings : PersistentStateComponent { + var preciseIncrementalEnabled: Boolean = true + var enableDaemon: Boolean = true + + override fun getState(): KotlinCompilerWorkspaceSettings { + return this + } + + override fun loadState(state: KotlinCompilerWorkspaceSettings?) { + XmlSerializerUtil.copyBean(state!!, this) + } +} diff --git a/idea/idea-android/build.gradle.kts b/idea/idea-android/build.gradle.kts index 752305196b2..4aa9da67a8f 100644 --- a/idea/idea-android/build.gradle.kts +++ b/idea/idea-android/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { testRuntime(project(":allopen-ide-plugin")) testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) testRuntime(intellijPluginDep("copyright")) testRuntime(intellijPluginDep("coverage")) testRuntime(intellijPluginDep("gradle")) diff --git a/idea/idea-android/build.gradle.kts.173 b/idea/idea-android/build.gradle.kts.173 new file mode 100644 index 00000000000..752305196b2 --- /dev/null +++ b/idea/idea-android/build.gradle.kts.173 @@ -0,0 +1,73 @@ + +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("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/KotlinAndroidLineMarkerProvider.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/KotlinAndroidLineMarkerProvider.kt index fcbc1a1c179..09d7d37e255 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/KotlinAndroidLineMarkerProvider.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/KotlinAndroidLineMarkerProvider.kt @@ -34,6 +34,7 @@ import com.intellij.ui.awt.RelativePoint import com.intellij.util.Function import org.jetbrains.android.dom.manifest.Manifest import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.android.resourceManagers.ModuleResourceManagers import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.psi.* @@ -95,7 +96,8 @@ class KotlinAndroidLineMarkerProvider : LineMarkerProvider { return } - val files = androidFacet + val files = ModuleResourceManagers + .getInstance(androidFacet) .localResourceManager .findResourcesByFieldName(resClassName, info.fieldName) .filterIsInstance() diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/KotlinAndroidLineMarkerProvider.kt.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/KotlinAndroidLineMarkerProvider.kt.173 new file mode 100644 index 00000000000..fcbc1a1c179 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/KotlinAndroidLineMarkerProvider.kt.173 @@ -0,0 +1,135 @@ +/* + * 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.SdkConstants +import com.android.resources.ResourceType +import com.intellij.codeHighlighting.Pass +import com.intellij.codeInsight.daemon.GutterIconNavigationHandler +import com.intellij.codeInsight.daemon.LineMarkerInfo +import com.intellij.codeInsight.daemon.LineMarkerProvider +import com.intellij.codeInsight.navigation.NavigationUtil +import com.intellij.icons.AllIcons +import com.intellij.ide.highlighter.XmlFileType +import com.intellij.navigation.GotoRelatedItem +import com.intellij.openapi.editor.markup.GutterIconRenderer +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.xml.XmlAttributeValue +import com.intellij.ui.awt.RelativePoint +import com.intellij.util.Function +import org.jetbrains.android.dom.manifest.Manifest +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import java.awt.event.MouseEvent +import javax.swing.Icon + + +class KotlinAndroidLineMarkerProvider : LineMarkerProvider { + override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? = null + + override fun collectSlowLineMarkers(elements: List, result: MutableCollection>) { + elements.forEach { + (it as? KtClass)?.getLineMarkerInfo()?.let { marker -> result.add(marker) } + } + } + + private fun KtClass.getLineMarkerInfo(): LineMarkerInfo? { + val nameIdentifier = nameIdentifier ?: return null + val androidFacet = AndroidFacet.getInstance(this) ?: return null + val manifest = androidFacet.manifest ?: return null + val manifestItems = collectGoToRelatedManifestItems(manifest) + if (manifestItems.isEmpty() && !isClassWithLayoutXml()) { + return null + } + + return LineMarkerInfo( + nameIdentifier, + nameIdentifier.textRange, + AllIcons.FileTypes.Xml, + Pass.LINE_MARKERS, + Function { "Related XML file" }, + GutterIconNavigationHandler { e: MouseEvent, _: PsiElement -> + NavigationUtil + .getRelatedItemsPopup( + manifestItems + collectGoToRelatedLayoutItems(androidFacet), + "Go to Related Files") + .show(RelativePoint(e)) + }, + GutterIconRenderer.Alignment.RIGHT) + } + + private fun KtClass.collectGoToRelatedLayoutItems(androidFacet: AndroidFacet): List { + val resources = mutableSetOf() + accept(object: KtVisitorVoid() { + override fun visitKtElement(element: KtElement) { + element.acceptChildren(this) + } + + override fun visitReferenceExpression(expression: KtReferenceExpression) { + super.visitReferenceExpression(expression) + + val resClassName = ResourceType.LAYOUT.getName() + val info = (expression as? KtSimpleNameExpression)?.let { + getReferredResourceOrManifestField(androidFacet, it, resClassName, true) + } + + if (info == null || info.isFromManifest) { + return + } + + val files = androidFacet + .localResourceManager + .findResourcesByFieldName(resClassName, info.fieldName) + .filterIsInstance() + + resources.addAll(files) + } + }) + + return resources.map { GotoRelatedLayoutItem(it) } + } + + private fun KtClass.collectGoToRelatedManifestItems(manifest: Manifest): List = + findComponentDeclarationInManifest(manifest)?.xmlAttributeValue?.let { listOf(GotoManifestItem(it)) } ?: emptyList() + + private class GotoManifestItem(attributeValue: XmlAttributeValue) : GotoRelatedItem(attributeValue) { + override fun getCustomName(): String? = "AndroidManifest.xml" + override fun getCustomContainerName(): String? = "" + override fun getCustomIcon(): Icon? = XmlFileType.INSTANCE.icon + } + + private class GotoRelatedLayoutItem(private val file: PsiFile) : GotoRelatedItem(file, "Layout Files") { + override fun getCustomContainerName(): String? = "(${file.containingDirectory.name})" + } + + companion object { + private val CLASSES_WITH_LAYOUT_XML = arrayOf( + SdkConstants.CLASS_ACTIVITY, + SdkConstants.CLASS_FRAGMENT, + SdkConstants.CLASS_V4_FRAGMENT, + "android.widget.Adapter") + + private fun KtClass.isClassWithLayoutXml(): Boolean { + val type = (unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as? ClassDescriptor)?.defaultType ?: return false + return CLASSES_WITH_LAYOUT_XML.any { type.isSubclassOf(it, true) } + } + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java b/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java index 6e1d66d4ba1..aa05125c069 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java @@ -23,8 +23,8 @@ import com.android.ide.common.resources.ResourceRepository; import com.android.ide.common.resources.ResourceResolver; 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; @@ -61,15 +61,14 @@ import java.awt.*; import java.io.File; import static com.android.SdkConstants.*; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_DRAWABLE; -import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE; -import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile; /** * Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator */ public class ResourceReferenceAnnotatorUtil { + + public static final int ICON_SIZE = 8; + @Nullable public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) { try { @@ -146,7 +145,7 @@ public class ResourceReferenceAnnotatorUtil { ResourceItem item = frameworkResources.getResourceItem(type, name); return item.getResourceValue(type, configuration.getFullConfig(), false); } else { - LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true); + AppResourceRepository appResources = AppResourceRepository.getOrCreateInstance(module); if (appResources == null) { return null; } @@ -161,26 +160,27 @@ public class ResourceReferenceAnnotatorUtil { @Nullable public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) { VirtualFile virtualFile = file.getVirtualFile(); - if (virtualFile == null) { + if(virtualFile == null) { return null; - } - - VirtualFile parent = virtualFile.getParent(); - if (parent == null) { - return null; - } - VirtualFile layout; - String parentName = parent.getName(); - if (!parentName.startsWith(FD_RES_LAYOUT)) { - layout = pickLayoutFile(module, facet); - if (layout == null) { - return null; - } } else { - layout = virtualFile; - } + VirtualFile parent = virtualFile.getParent(); + if(parent == null) { + return null; + } else { + String parentName = parent.getName(); + VirtualFile layout; + if(!parentName.startsWith("layout")) { + layout = ResourceHelper.pickAnyLayoutFile(module, facet); + if(layout == null) { + return null; + } + } else { + layout = virtualFile; + } - return facet.getConfigurationManager().getConfiguration(layout); + return ConfigurationManager.getOrCreateInstance(module).getConfiguration(layout); + } + } } public static class ColorRenderer extends GutterIconRenderer { diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.173 new file mode 100644 index 00000000000..6e1d66d4ba1 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/ResourceReferenceAnnotatorUtil.java.173 @@ -0,0 +1,270 @@ +/* + * 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.ide.common.rendering.api.ResourceValue; +import com.android.ide.common.resources.ResourceItem; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.common.resources.ResourceResolver; +import com.android.resources.ResourceType; +import com.android.tools.idea.configurations.Configuration; +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; +import com.google.common.base.Charsets; +import com.google.common.io.Files; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ui.ColorIcon; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.JBUI; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import java.awt.*; +import java.io.File; + +import static com.android.SdkConstants.*; +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_DRAWABLE; +import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE; +import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile; + +/** + * Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator + */ +public class ResourceReferenceAnnotatorUtil { + @Nullable + public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) { + try { + String xml = Files.toString(file, Charsets.UTF_8); + Document document = XmlUtils.parseDocumentSilently(xml, true); + if (document != null && document.getDocumentElement() != null) { + Element root = document.getDocumentElement(); + String tag = root.getTagName(); + Element target = null; + String attribute = null; + if ("vector".equals(tag)) { + // Vectors are handled in the icon cache + return file; + } + else if ("bitmap".equals(tag) || "nine-patch".equals(tag)) { + target = root; + attribute = ATTR_SRC; + } + else if ("selector".equals(tag) || + "level-list".equals(tag) || + "layer-list".equals(tag) || + "transition".equals(tag)) { + NodeList children = root.getChildNodes(); + for (int i = children.getLength() - 1; i >= 0; i--) { + Node item = children.item(i); + if (item.getNodeType() == Node.ELEMENT_NODE && TAG_ITEM.equals(item.getNodeName())) { + target = (Element)item; + if (target.hasAttributeNS(ANDROID_URI, ATTR_DRAWABLE)) { + attribute = ATTR_DRAWABLE; + break; + } + } + } + } + else if ("clip".equals(tag) || "inset".equals(tag) || "scale".equals(tag)) { + target = root; + attribute = ATTR_DRAWABLE; + } else { + // etc - no bitmap to be found + return null; + } + if (attribute != null && target.hasAttributeNS(ANDROID_URI, attribute)) { + String src = target.getAttributeNS(ANDROID_URI, attribute); + ResourceValue value = resourceResolver.findResValue(src, false); + if (value != null) { + return ResourceHelper.resolveDrawable(resourceResolver, value, project); + + } + } + } + } catch (Throwable ignore) { + // Not logging for now; afraid to risk unexpected crashes in upcoming preview. TODO: Re-enable. + //Logger.getInstance(AndroidColorAnnotator.class).warn(String.format("Could not read/render icon image %1$s", file), e); + } + + return null; + } + + /** Looks up the resource item of the given type and name for the given configuration, if any */ + @Nullable + public static ResourceValue findResourceValue(ResourceType type, + String name, + boolean isFramework, + Module module, + Configuration configuration) { + if (isFramework) { + ResourceRepository frameworkResources = configuration.getFrameworkResources(); + if (frameworkResources == null) { + return null; + } + if (!frameworkResources.hasResourceItem(type, name)) { + return null; + } + ResourceItem item = frameworkResources.getResourceItem(type, name); + return item.getResourceValue(type, configuration.getFullConfig(), false); + } else { + LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true); + if (appResources == null) { + return null; + } + if (!appResources.hasResourceItem(type, name)) { + return null; + } + return appResources.getConfiguredValue(type, name, configuration.getFullConfig()); + } + } + + /** Picks a suitable configuration to use for resource resolution */ + @Nullable + public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) { + VirtualFile virtualFile = file.getVirtualFile(); + if (virtualFile == null) { + return null; + } + + VirtualFile parent = virtualFile.getParent(); + if (parent == null) { + return null; + } + VirtualFile layout; + String parentName = parent.getName(); + if (!parentName.startsWith(FD_RES_LAYOUT)) { + layout = pickLayoutFile(module, facet); + if (layout == null) { + return null; + } + } else { + layout = virtualFile; + } + + return facet.getConfigurationManager().getConfiguration(layout); + } + + public static class ColorRenderer extends GutterIconRenderer { + private final PsiElement myElement; + private final Color myColor; + + ColorRenderer(@NotNull PsiElement element, @Nullable Color color) { + myElement = element; + myColor = color; + } + + @NotNull + @Override + public Icon getIcon() { + Color color = getCurrentColor(); + return JBUI.scale(color == null ? EmptyIcon.create(ICON_SIZE) : new ColorIcon(ICON_SIZE, color)); + } + + @Nullable + private Color getCurrentColor() { + if (myColor != null) { + return myColor; + } else if (myElement instanceof XmlTag) { + return ResourceHelper.parseColor(((XmlTag)myElement).getValue().getText()); + } else if (myElement instanceof XmlAttributeValue) { + return ResourceHelper.parseColor(((XmlAttributeValue)myElement).getValue()); + } else { + return null; + } + } + + @Override + public AnAction getClickAction() { + if (myColor != null) { // Cannot set colors that were derived + return null; + } + return new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); + if (editor != null) { + // Need ARGB support in platform color chooser; see + // https://youtrack.jetbrains.com/issue/IDEA-123498 + //final Color color = + // ColorChooser.chooseColor(editor.getComponent(), AndroidBundle.message("android.choose.color"), getCurrentColor()); + final Color color = ColorPicker.showDialog(editor.getComponent(), "Choose Color", getCurrentColor(), true, null, false); + if (color != null) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + if (myElement instanceof XmlTag) { + ((XmlTag)myElement).getValue().setText(ResourceHelper.colorToString(color)); + } else if (myElement instanceof XmlAttributeValue) { + XmlAttribute attribute = PsiTreeUtil.getParentOfType(myElement, XmlAttribute.class); + if (attribute != null) { + attribute.setValue(ResourceHelper.colorToString(color)); + } + } + } + }); + } + } + } + }; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ColorRenderer that = (ColorRenderer)o; + // TODO: Compare with modification count in app resources (if not framework) + if (myColor != null ? !myColor.equals(that.myColor) : that.myColor != null) return false; + if (!myElement.equals(that.myElement)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myElement.hashCode(); + result = 31 * result + (myColor != null ? myColor.hashCode() : 0); + return result; + } + } +} diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt index 0cafa358089..40c99f83900 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt @@ -22,6 +22,7 @@ import com.intellij.openapi.roots.ProjectRootModificationTracker import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.android.sdk.AndroidSdkData import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad import java.io.File @@ -56,7 +57,7 @@ class AndroidDexerImpl(val project: Project) : AndroidDexer { private fun doGetAndroidDexFile(): File? { for (module in ModuleManager.getInstance(project).modules) { val androidFacet = AndroidFacet.getInstance(module) ?: continue - val sdkData = androidFacet.sdkData ?: continue + val sdkData = AndroidSdkData.getSdkData(androidFacet) ?: continue val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false) ?: sdkData.getLatestBuildTool(/* allowPreview = */ true) ?: continue diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt.173 new file mode 100644 index 00000000000..0cafa358089 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/debugger/AndroidDexerImpl.kt.173 @@ -0,0 +1,72 @@ +/* + * 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.debugger + +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer +import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad +import java.io.File +import java.net.URLClassLoader +import java.security.ProtectionDomain + +class AndroidDexerImpl(val project: Project) : AndroidDexer { + private val cachedDexWrapper = CachedValuesManager.getManager(project).createCachedValue({ + val dexWrapper = doGetAndroidDexFile()?.let { dexJarFile -> + val androidDexWrapperName = AndroidDexWrapper::class.java.canonicalName + val classBytes = this.javaClass.classLoader.getResource( + androidDexWrapperName.replace('.', '/') + ".class").readBytes() + + val dexClassLoader = object : URLClassLoader(arrayOf(dexJarFile.toURI().toURL()), this::class.java.classLoader) { + init { + defineClass(androidDexWrapperName, classBytes, 0, classBytes.size, null as ProtectionDomain?) + } + } + + Class.forName(androidDexWrapperName, true, dexClassLoader).newInstance() + } + + CachedValueProvider.Result.createSingleDependency(dexWrapper, ProjectRootModificationTracker.getInstance(project)) + }, /* trackValue = */ false) + + override fun dex(classes: Collection): ByteArray? { + val dexWrapper = cachedDexWrapper.value + val dexMethod = dexWrapper::class.java.methods.firstOrNull { it.name == "dex" } ?: return null + return dexMethod.invoke(dexWrapper, classes) as? ByteArray ?: return null + } + + private fun doGetAndroidDexFile(): File? { + for (module in ModuleManager.getInstance(project).modules) { + val androidFacet = AndroidFacet.getInstance(module) ?: continue + val sdkData = androidFacet.sdkData ?: continue + val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false) + ?: sdkData.getLatestBuildTool(/* allowPreview = */ true) + ?: continue + + val dxJar = File(latestBuildTool.location, "lib/dx.jar") + if (dxJar.exists()) { + return dxJar + } + } + + return null + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt index 8d4992a0c04..30255ec65f7 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt @@ -47,7 +47,6 @@ class ResourceFoldingBuilder : FoldingBuilderEx() { // See lint's StringFormatDetector private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])") private val FOLD_MAX_LENGTH = 60 - private val FORCE_PROJECT_RESOURCE_LOADING = true private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode private val RESOURCE_TYPES = listOf(ResourceType.STRING, ResourceType.DIMEN, @@ -254,6 +253,6 @@ class ResourceFoldingBuilder : FoldingBuilderEx() { } private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let { - AppResourceRepository.getAppResources(it, FORCE_PROJECT_RESOURCE_LOADING) + AppResourceRepository.findExistingInstance(it) } } diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt.173 new file mode 100644 index 00000000000..8d4992a0c04 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/folding/ResourceFoldingBuilder.kt.173 @@ -0,0 +1,259 @@ +/* + * 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.folding + +import com.android.SdkConstants.* +import com.android.ide.common.resources.configuration.FolderConfiguration +import com.android.ide.common.resources.configuration.LocaleQualifier +import com.android.resources.ResourceType +import com.android.tools.idea.folding.AndroidFoldingSettings +import com.android.tools.idea.res.AppResourceRepository +import com.android.tools.idea.res.LocalResourceRepository +import com.intellij.lang.ASTNode +import com.intellij.lang.folding.FoldingBuilderEx +import com.intellij.lang.folding.FoldingDescriptor +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.editor.Document +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.source.SourceTreeToPsiMap +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.uast.* +import org.jetbrains.uast.visitor.AbstractUastVisitor +import java.util.regex.Pattern + + +class ResourceFoldingBuilder : FoldingBuilderEx() { + + companion object { + // See lint's StringFormatDetector + private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])") + private val FOLD_MAX_LENGTH = 60 + private val FORCE_PROJECT_RESOURCE_LOADING = true + private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode + private val RESOURCE_TYPES = listOf(ResourceType.STRING, + ResourceType.DIMEN, + ResourceType.INTEGER, + ResourceType.PLURALS) + } + + private val isFoldingEnabled = AndroidFoldingSettings.getInstance().isCollapseAndroidStrings + + override fun getPlaceholderText(node: ASTNode): String? { + + tailrec fun UElement.unwrapReferenceAndGetValue(resources: LocalResourceRepository): String? = when (this) { + is UQualifiedReferenceExpression -> selector.unwrapReferenceAndGetValue(resources) + is UCallExpression -> (valueArguments.firstOrNull() as? UReferenceExpression)?.getAndroidResourceValue(resources, this) + else -> (this as? UReferenceExpression)?.getAndroidResourceValue(resources) + } + + val element = SourceTreeToPsiMap.treeElementToPsi(node) ?: return null + val appResources = getAppResources(element) ?: return null + val uastContext = ServiceManager.getService(element.project, UastContext::class.java) ?: return null + return uastContext.convertElement(element, null, null)?.unwrapReferenceAndGetValue(appResources) + } + + override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array { + if (root !is KtFile || quick && !UNIT_TEST_MODE || !isFoldingEnabled || AndroidFacet.getInstance(root) == null) { + return emptyArray() + } + + val file = root.toUElement() + val result = arrayListOf() + file?.accept(object : AbstractUastVisitor() { + override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean { + node.getFoldingDescriptor()?.let { result.add(it) } + return super.visitSimpleNameReferenceExpression(node) + } + }) + + return result.toTypedArray() + } + + override fun isCollapsedByDefault(node: ASTNode): Boolean = isFoldingEnabled + + private fun UReferenceExpression.getFoldingDescriptor(): FoldingDescriptor? { + val resolved = resolve() ?: return null + val resourceType = resolved.getAndroidResourceType() ?: return null + if (resourceType !in RESOURCE_TYPES) return null + + fun UElement.createFoldingDescriptor() = psi?.let { psi -> + val dependencies: Set = setOf(psi) + FoldingDescriptor(psi.node, psi.textRange, null, dependencies) + } + + val element = uastParent as? UQualifiedReferenceExpression ?: this + val getResourceValueCall = (element.uastParent as? UCallExpression)?.takeIf { it.isFoldableGetResourceValueCall() } + if (getResourceValueCall != null) { + val qualifiedCall = getResourceValueCall.uastParent as? UQualifiedReferenceExpression + if (qualifiedCall?.selector == getResourceValueCall) { + return qualifiedCall.createFoldingDescriptor() + } + + return getResourceValueCall.createFoldingDescriptor() + } + + return element.createFoldingDescriptor() + } + + private fun UCallExpression.isFoldableGetResourceValueCall(): Boolean { + return methodName == "getString" || + methodName == "getText" || + methodName == "getInteger" || + methodName?.startsWith("getDimension") ?: false || + methodName?.startsWith("getQuantityString") ?: false + } + + private fun PsiElement.getAndroidResourceType(): ResourceType? { + val elementType = parent as? PsiClass ?: return null + val elementPackage = elementType.parent as? PsiClass ?: return null + if (R_CLASS != elementPackage.name) return null + if (elementPackage.qualifiedName != "$ANDROID_PKG.$R_CLASS") { + return ResourceType.getEnum(elementType.name) + } + + return null + } + + private fun UReferenceExpression.getAndroidResourceValue(resources: LocalResourceRepository, call: UCallExpression? = null): String? { + val resourceType = resolve()?.getAndroidResourceType() ?: return null + val referenceConfig = FolderConfiguration().apply { localeQualifier = LocaleQualifier("xx") } + val key = resolvedName ?: return null + val resourceValue = resources.getResourceValue(resourceType, key, referenceConfig) ?: return null + val text = if (call != null) formatArguments(call, resourceValue) else resourceValue + + if (resourceType == ResourceType.STRING || resourceType == ResourceType.PLURALS) { + return '"' + StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH - 2, 0) + '"' + } + else if (text.length <= 1) { + // Don't just inline empty or one-character replacements: they can't be expanded by a mouse click + // so are hard to use without knowing about the folding keyboard shortcut to toggle folding. + // This is similar to how IntelliJ 14 handles call parameters + return key + ": " + text + } + + return StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH, 0) + } + + private tailrec fun LocalResourceRepository.getResourceValue( + type: ResourceType, + name: String, + referenceConfig: FolderConfiguration): String? { + val value = getConfiguredValue(type, name, referenceConfig)?.value ?: return null + if (!value.startsWith('@')) { + return value + } + + val (referencedTypeName, referencedName) = value.substring(1).split('/').takeIf { it.size == 2 } ?: return value + val referencedType = ResourceType.getEnum(referencedTypeName) ?: return value + return getResourceValue(referencedType, referencedName, referenceConfig) + } + + // Converted from com.android.tools.idea.folding.InlinedResource#insertArguments + private fun formatArguments(callExpression: UCallExpression, formatString: String): String { + if (!formatString.contains('%')) { + return formatString + } + + val args = callExpression.valueArguments + if (args.isEmpty() || !args.first().isPsiValid) { + return formatString + } + + val matcher = FORMAT.matcher(formatString) + var index = 0 + var prevIndex = 0 + var nextNumber = 1 + var start = 0 + val sb = StringBuilder(2 * formatString.length) + while (true) { + if (matcher.find(index)) { + if ("%" == matcher.group(6)) { + index = matcher.end() + continue + } + val matchStart = matcher.start() + // Make sure this is not an escaped '%' + while (prevIndex < matchStart) { + val c = formatString[prevIndex] + if (c == '\\') { + prevIndex++ + } + prevIndex++ + } + if (prevIndex > matchStart) { + // We're in an escape, ignore this result + index = prevIndex + continue + } + + index = matcher.end() + + // Shouldn't throw a number format exception since we've already + // matched the pattern in the regexp + val number: Int + var numberString: String? = matcher.group(1) + if (numberString != null) { + // Strip off trailing $ + numberString = numberString.substring(0, numberString.length - 1) + number = Integer.parseInt(numberString) + nextNumber = number + 1 + } + else { + number = nextNumber++ + } + + if (number > 0 && number < args.size) { + val argExpression = args[number] + var value: Any? = argExpression.evaluate() + + if (value == null) { + value = args[number].asSourceString() + } + + for (i in start..matchStart - 1) { + sb.append(formatString[i]) + } + + sb.append("{") + sb.append(value) + sb.append('}') + start = index + } + } + else { + var i = start + val n = formatString.length + while (i < n) { + sb.append(formatString[i]) + i++ + } + break + } + } + + return sb.toString() + } + + private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let { + AppResourceRepository.getAppResources(it, FORCE_PROJECT_RESOURCE_LOADING) + } +} diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt index 18d8a751604..a71ac0416b7 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.android.inspection +import com.android.tools.idea.model.AndroidModuleInfo import com.intellij.codeInspection.* import com.intellij.openapi.project.Project import com.intellij.psi.PsiElementVisitor @@ -28,7 +29,11 @@ import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { - val compileSdk = AndroidFacet.getInstance(session.file)?.androidModuleInfo?.buildSdkVersion?.apiLevel + val compileSdk = AndroidFacet.getInstance(session.file) + ?.let { facet -> AndroidModuleInfo.getInstance(facet) } + ?.buildSdkVersion + ?.apiLevel + if (compileSdk == null || compileSdk < 26) { return KtVisitorVoid() } diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt.173 new file mode 100644 index 00000000000..18d8a751604 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/TypeParameterFindViewByIdInspection.kt.173 @@ -0,0 +1,78 @@ +/* + * 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.inspection + +import com.intellij.codeInspection.* +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtPsiUtil.isUnsafeCast +import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument + +class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + val compileSdk = AndroidFacet.getInstance(session.file)?.androidModuleInfo?.buildSdkVersion?.apiLevel + if (compileSdk == null || compileSdk < 26) { + return KtVisitorVoid() + } + + return object : KtVisitorVoid() { + override fun visitCallExpression(expression: KtCallExpression) { + super.visitCallExpression(expression) + if (expression.calleeExpression?.text != "findViewById" || expression.typeArguments.isNotEmpty()) { + return + } + + val parentCast = (expression.parent as? KtBinaryExpressionWithTypeRHS)?.takeIf { isUnsafeCast(it) } ?: return + val typeText = parentCast.right?.getTypeTextWithoutQuestionMark() ?: return + val callableDescriptor = expression.resolveToCall()?.resultingDescriptor ?: return + if (callableDescriptor.name.asString() != "findViewById" || callableDescriptor.typeParameters.size != 1) { + return + } + + holder.registerProblem( + parentCast, + "Can be converted to findViewById<$typeText>(...)", + ConvertCastToFindViewByIdWithTypeParameter()) + } + } + } + + class ConvertCastToFindViewByIdWithTypeParameter : LocalQuickFix { + override fun getFamilyName(): String = "Convert cast to findViewById with type parameter" + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val cast = descriptor.psiElement as? KtBinaryExpressionWithTypeRHS ?: return + val typeText = cast.right?.getTypeTextWithoutQuestionMark() ?: return + val call = cast.left as? KtCallExpression ?: return + + val newCall = call.copy() as KtCallExpression + val typeArgument = KtPsiFactory(call).createTypeArgument(typeText) + newCall.addTypeArgument(typeArgument) + + cast.replace(newCall) + } + } + + companion object { + fun KtTypeReference.getTypeTextWithoutQuestionMark(): String? = + (typeElement as? KtNullableType)?.innerType?.text ?: typeElement?.text + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java b/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java index 0410dfb1624..89243e861e2 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java @@ -29,6 +29,7 @@ import org.jetbrains.android.dom.resources.Attr; import org.jetbrains.android.dom.resources.DeclareStyleable; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.resourceManagers.LocalResourceManager; +import org.jetbrains.android.resourceManagers.ModuleResourceManagers; import org.jetbrains.android.resourceManagers.ResourceManager; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; @@ -68,9 +69,10 @@ public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandl collectManifestElements(nestedClassName, fieldName, facet, resourceList); } else { + ModuleResourceManagers managers = ModuleResourceManagers.getInstance(facet); ResourceManager manager = info.isSystem() - ? facet.getSystemResourceManager(false) - : facet.getLocalResourceManager(); + ? managers.getSystemResourceManager(false) + : managers.getLocalResourceManager(); if (manager == null) { return null; } diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java.173 b/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java.173 new file mode 100644 index 00000000000..0410dfb1624 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/navigation/KotlinAndroidGotoDeclarationHandler.java.173 @@ -0,0 +1,147 @@ +/* + * 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 com.android.resources.ResourceType; +import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlElement; +import org.jetbrains.android.dom.AndroidAttributeValue; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.dom.manifest.ManifestElementWithRequiredName; +import org.jetbrains.android.dom.resources.Attr; +import org.jetbrains.android.dom.resources.DeclareStyleable; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.resourceManagers.LocalResourceManager; +import org.jetbrains.android.resourceManagers.ResourceManager; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.android.AndroidUtilKt; +import org.jetbrains.kotlin.psi.KtSimpleNameExpression; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +// TODO: ask for extension point +// this class is mostly copied from org.jetbrains.android.AndroidGotoDeclarationHandler +public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandler { + @Override + public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) { + KtSimpleNameExpression referenceExpression = GotoResourceHelperKt.getReferenceExpression(sourceElement); + if (referenceExpression == null) { + return null; + } + + AndroidFacet facet = AndroidUtilKt.getAndroidFacetForFile(referenceExpression); + if (facet == null) { + return null; + } + + AndroidResourceUtil.MyReferredResourceFieldInfo info = GotoResourceHelperKt.getInfo(referenceExpression, facet); + + if (info == null) return null; + + String nestedClassName = info.getClassName(); + String fieldName = info.getFieldName(); + List resourceList = new ArrayList(); + + if (info.isFromManifest()) { + collectManifestElements(nestedClassName, fieldName, facet, resourceList); + } + else { + ResourceManager manager = info.isSystem() + ? facet.getSystemResourceManager(false) + : facet.getLocalResourceManager(); + if (manager == null) { + return null; + } + manager.collectLazyResourceElements(nestedClassName, fieldName, false, referenceExpression, resourceList); + + if (manager instanceof LocalResourceManager) { + LocalResourceManager lrm = (LocalResourceManager) manager; + + if (nestedClassName.equals(ResourceType.ATTR.getName())) { + for (Attr attr : lrm.findAttrs(fieldName)) { + resourceList.add(attr.getName().getXmlAttributeValue()); + } + } + else if (nestedClassName.equals(ResourceType.STYLEABLE.getName())) { + for (DeclareStyleable styleable : lrm.findStyleables(fieldName)) { + resourceList.add(styleable.getName().getXmlAttributeValue()); + } + + for (Attr styleable : lrm.findStyleableAttributesByFieldName(fieldName)) { + resourceList.add(styleable.getName().getXmlAttributeValue()); + } + } + } + } + + if (resourceList.size() > 1) { + // Sort to ensure the output is stable, and to prefer the base folders + Collections.sort(resourceList, AndroidResourceUtil.RESOURCE_ELEMENT_COMPARATOR); + } + + return resourceList.toArray(new PsiElement[resourceList.size()]); + } + + private static void collectManifestElements( + @NotNull String nestedClassName, + @NotNull String fieldName, + @NotNull AndroidFacet facet, + @NotNull List result + ) { + Manifest manifest = facet.getManifest(); + + if (manifest == null) { + return; + } + List list; + + if ("permission".equals(nestedClassName)) { + list = manifest.getPermissions(); + } + else if ("permission_group".equals(nestedClassName)) { + list = manifest.getPermissionGroups(); + } + else { + return; + } + for (ManifestElementWithRequiredName domElement : list) { + AndroidAttributeValue nameAttribute = domElement.getName(); + String name = nameAttribute.getValue(); + + if (AndroidUtils.equal(name, fieldName, false)) { + XmlElement psiElement = nameAttribute.getXmlAttributeValue(); + + if (psiElement != null) { + result.add(psiElement); + } + } + } + } + + @Override + public String getActionText(DataContext context) { + return null; + } +} diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetApiQuickFix.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetApiQuickFix.kt new file mode 100644 index 00000000000..a05fc15de40 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetApiQuickFix.kt @@ -0,0 +1,95 @@ +/* + * 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.android.SdkConstants +import com.android.tools.lint.checks.ApiDetector.REQUIRES_API_ANNOTATION +import com.intellij.codeInsight.FileModificationService +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.android.util.AndroidBundle +import org.jetbrains.kotlin.android.hasBackingField +import org.jetbrains.kotlin.idea.util.addAnnotation +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* + + +class AddTargetApiQuickFix( + val api: Int, + val useRequiresApi: Boolean +) : AndroidLintQuickFix { + + private companion object { + val FQNAME_TARGET_API = FqName(SdkConstants.FQCN_TARGET_API) + val FQNAME_REQUIRES_API = FqName(REQUIRES_API_ANNOTATION) + } + + override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean = + getAnnotationContainer(startElement, useRequiresApi) != null + + override fun getName(): String = getAnnotationValue(false).let { + if (useRequiresApi) { + // Not Available in Android plugin 2.0 + // AndroidBundle.message("android.lint.fix.add.requires.api", it) + "Add @RequiresApi($it) Annotation" + } else { + AndroidBundle.message("android.lint.fix.add.target.api", it) + } + } + + override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) { + val annotationContainer = getAnnotationContainer(startElement, useRequiresApi) ?: return + if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) { + return + } + + if (annotationContainer is KtModifierListOwner) { + annotationContainer.addAnnotation( + if (useRequiresApi) FQNAME_REQUIRES_API else FQNAME_TARGET_API, + getAnnotationValue(true), + whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ") + } + } + + private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter || this is KtPropertyAccessor) + + private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified) + + private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean): PsiElement? { + return PsiTreeUtil.findFirstParent(element) { + if (useRequiresApi) + it.isRequiresApiAnnotationValidTarget() + else + it.isTargetApiAnnotationValidTarget() + } + } + + private fun PsiElement.isRequiresApiAnnotationValidTarget(): Boolean { + return this is KtClassOrObject || + (this is KtFunction && this !is KtFunctionLiteral) || + (this is KtProperty && !isLocal && hasBackingField()) || + this is KtPropertyAccessor + } + + private fun PsiElement.isTargetApiAnnotationValidTarget(): Boolean { + return this is KtClassOrObject || + (this is KtFunction && this !is KtFunctionLiteral) || + this is KtPropertyAccessor + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetVersionCheckQuickFix.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetVersionCheckQuickFix.kt new file mode 100644 index 00000000000..d506bedc907 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/AddTargetVersionCheckQuickFix.kt @@ -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)[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/src/org/jetbrains/kotlin/android/quickfix/ApiUtils.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/ApiUtils.kt new file mode 100644 index 00000000000..3a52671ba84 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/ApiUtils.kt @@ -0,0 +1,24 @@ +/* + * 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.android.sdklib.SdkVersionInfo.* + + +fun getVersionField(api: Int, fullyQualified: Boolean): String = getBuildCode(api)?.let { + if (fullyQualified) "android.os.Build.VERSION_CODES.$it" else it +} ?: api.toString() \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/KotlinAndroidQuickFixProvider.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/KotlinAndroidQuickFixProvider.kt new file mode 100644 index 00000000000..68feed44bda --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/KotlinAndroidQuickFixProvider.kt @@ -0,0 +1,80 @@ +/* + * 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.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX +import com.android.tools.lint.checks.ApiDetector +import com.android.tools.lint.checks.CommentDetector +import com.android.tools.lint.checks.ParcelDetector +import com.android.tools.lint.detector.api.Issue +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElement +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.android.inspections.lint.AndroidLintQuickFix +import org.jetbrains.android.inspections.lint.AndroidLintQuickFixProvider +import java.util.regex.Pattern + + +class KotlinAndroidQuickFixProvider : AndroidLintQuickFixProvider { + override fun getQuickFixes( + issue: Issue, + startElement: PsiElement, + endElement: PsiElement, + message: String, + data: Any? + ): Array { + val fixes: Array = when (issue) { + ApiDetector.UNSUPPORTED, ApiDetector.INLINED -> getApiQuickFixes(issue, startElement, message) + ParcelDetector.ISSUE -> arrayOf(ParcelableQuickFix()) + else -> emptyArray() + } + + if (issue != CommentDetector.STOP_SHIP) { + return fixes + SuppressLintQuickFix(issue.id) + } + + return fixes + } + + fun getApiQuickFixes(issue: Issue, element: PsiElement, message: String): Array { + val api = getRequiredVersion(message) + if (api == -1) { + return AndroidLintQuickFix.EMPTY_ARRAY + } + + val project = element.project + if (JavaPsiFacade.getInstance(project).findClass(REQUIRES_API_ANNOTATION, GlobalSearchScope.allScope(project)) != null) { + return arrayOf(AddTargetApiQuickFix(api, true), AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api)) + } + + return arrayOf(AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api)) + } + + private fun getRequiredVersion(errorMessage: String): Int { + val pattern = Pattern.compile("\\s(\\d+)\\s") + val matcher = pattern.matcher(errorMessage) + if (matcher.find()) { + return Integer.parseInt(matcher.group(1)) + } + + return -1 + } + + companion object { + val REQUIRES_API_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresApi" + } +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/ParcelableQuickFix.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/ParcelableQuickFix.kt new file mode 100644 index 00000000000..d3442300f5e --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/ParcelableQuickFix.kt @@ -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.quickfix + +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.android.util.AndroidBundle +import org.jetbrains.kotlin.android.canAddParcelable +import org.jetbrains.kotlin.android.implementParcelable +import org.jetbrains.kotlin.android.isParcelize +import org.jetbrains.kotlin.psi.KtClass + + +class ParcelableQuickFix : AndroidLintQuickFix { + override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) { + startElement.getTargetClass()?.implementParcelable() + } + + override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean { + val targetClass = startElement.getTargetClass() ?: return false + return targetClass.canAddParcelable() && !targetClass.isParcelize() + } + + override fun getName(): String = AndroidBundle.message("implement.parcelable.intention.text") + + private fun PsiElement.getTargetClass(): KtClass? = PsiTreeUtil.getParentOfType(this, KtClass::class.java, false) +} \ No newline at end of file diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/SuppressLintQuickFix.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/SuppressLintQuickFix.kt new file mode 100644 index 00000000000..724f718c257 --- /dev/null +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/quickfix/SuppressLintQuickFix.kt @@ -0,0 +1,99 @@ +/* + * 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.android.SdkConstants +import com.intellij.codeInsight.FileModificationService +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.android.util.AndroidBundle +import org.jetbrains.kotlin.android.hasBackingField +import org.jetbrains.kotlin.idea.util.addAnnotation +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* + + +class SuppressLintQuickFix(id: String) : AndroidLintQuickFix { + private val lintId = getLintId(id) + + override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) { + val annotationContainer = PsiTreeUtil.findFirstParent(startElement, true) { it.isSuppressLintTarget() } ?: return + if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) { + return + } + + val argument = "\"$lintId\"" + + when (annotationContainer) { + is KtModifierListOwner -> { + annotationContainer.addAnnotation( + FQNAME_SUPPRESS_LINT, + argument, + whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ", + addToExistingAnnotation = { entry -> addArgumentToAnnotation(entry, argument) }) + } + } + } + + override fun getName(): String = AndroidBundle.message(SUPPRESS_LINT_MESSAGE, lintId) + + override fun isApplicable( + startElement: PsiElement, + endElement: PsiElement, + contextType: AndroidQuickfixContexts.ContextType + ): Boolean = true + + private fun addArgumentToAnnotation(entry: KtAnnotationEntry, argument: String): Boolean { + // add new arguments to an existing entry + val args = entry.valueArgumentList + val psiFactory = KtPsiFactory(entry) + val newArgList = psiFactory.createCallArguments("($argument)") + when { + args == null -> // new argument list + entry.addAfter(newArgList, entry.lastChild) + args.arguments.isEmpty() -> // replace '()' with a new argument list + args.replace(newArgList) + args.arguments.none { it.textMatches(argument) } -> + args.addArgument(newArgList.arguments[0]) + } + + return true + } + + private fun getLintId(intentionId: String) = + if (intentionId.startsWith(INTENTION_NAME_PREFIX)) intentionId.substring(INTENTION_NAME_PREFIX.length) else intentionId + + private fun KtElement.isNewLineNeededForAnnotation(): Boolean { + return !(this is KtParameter || + this is KtTypeParameter || + this is KtPropertyAccessor) + } + + private fun PsiElement.isSuppressLintTarget(): Boolean { + return this is KtDeclaration && + (this as? KtProperty)?.hasBackingField() ?: true && + this !is KtFunctionLiteral && + this !is KtDestructuringDeclaration + } + private companion object { + val INTENTION_NAME_PREFIX = "AndroidLint" + val SUPPRESS_LINT_MESSAGE = "android.lint.fix.suppress.lint.api.annotation" + val FQNAME_SUPPRESS_LINT = FqName(SdkConstants.FQCN_SUPPRESS_LINT) + } +} \ No newline at end of file diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt b/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt index 0acc40b7c13..b85ee638965 100644 --- a/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt +++ b/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.android.lint import com.intellij.codeInspection.InspectionProfileEntry import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl import com.intellij.util.PathUtil -import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase +import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase import org.jetbrains.kotlin.android.KotlinAndroidTestCase import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.test.InTextDirectivesUtils diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt.173 b/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt.173 new file mode 100644 index 00000000000..0acc40b7c13 --- /dev/null +++ b/idea/idea-android/tests/org/jetbrains/kotlin/android/lint/AbstractKotlinLintTest.kt.173 @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2016 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.lint + +import com.intellij.codeInspection.InspectionProfileEntry +import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl +import com.intellij.util.PathUtil +import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase +import org.jetbrains.kotlin.android.KotlinAndroidTestCase +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes +import java.io.File + +abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() { + + override fun setUp() { + super.setUp() + AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap() + (myFixture as CodeInsightTestFixtureImpl).setVirtualFileFilter { false } // Allow access to tree elements. + ConfigLibraryUtil.configureKotlinRuntime(myModule) + ConfigLibraryUtil.addLibrary(myModule, "androidExtensionsRuntime", "dist/kotlinc/lib", arrayOf("android-extensions-runtime.jar")) + } + + override fun tearDown() { + ConfigLibraryUtil.unConfigureKotlinRuntime(myModule) + ConfigLibraryUtil.removeLibrary(myModule, "androidExtensionsRuntime") + super.tearDown() + } + + fun doTest(path: String) { + val ktFile = File(path) + val fileText = ktFile.readText() + val mainInspectionClassName = findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty class name") + val dependencies = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// DEPENDENCY: ") + + val inspectionClassNames = mutableListOf(mainInspectionClassName) + for (i in 2..100) { + val className = findStringWithPrefixes(ktFile.readText(), "// INSPECTION_CLASS$i: ") ?: break + inspectionClassNames += className + } + + myFixture.enableInspections(*inspectionClassNames.map { className -> + val inspectionClass = Class.forName(className) + inspectionClass.newInstance() as InspectionProfileEntry + }.toTypedArray()) + + val additionalResourcesDir = File(ktFile.parentFile, getTestName(true)) + if (additionalResourcesDir.exists()) { + for (file in additionalResourcesDir.listFiles()) { + if (file.isFile) { + myFixture.copyFileToProject(file.absolutePath, file.name) + } + else if (file.isDirectory) { + myFixture.copyDirectoryToProject(file.absolutePath, file.name) + } + } + } + + val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/${PathUtil.getFileName(path)}") + myFixture.configureFromExistingVirtualFile(virtualFile) + + dependencies.forEach { dependency -> + val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim) + myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath") + } + + myFixture.checkHighlighting(true, false, true) + } +} \ No newline at end of file diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt b/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt index b46f1d76409..c2f6989d0bc 100644 --- a/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt +++ b/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.android.quickfix import com.intellij.codeInspection.InspectionProfileEntry import com.intellij.openapi.util.io.FileUtil import com.intellij.util.PathUtil -import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase +import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase import org.jetbrains.kotlin.android.KotlinAndroidTestCase import org.jetbrains.kotlin.test.InTextDirectivesUtils import java.io.File diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt.173 b/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt.173 new file mode 100644 index 00000000000..b46f1d76409 --- /dev/null +++ b/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AbstractAndroidLintQuickfixTest.kt.173 @@ -0,0 +1,62 @@ +/* + * 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.codeInspection.InspectionProfileEntry +import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.PathUtil +import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase +import org.jetbrains.kotlin.android.KotlinAndroidTestCase +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import java.io.File + + +abstract class AbstractAndroidLintQuickfixTest : KotlinAndroidTestCase() { + + override fun setUp() { + AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap() + super.setUp() + } + + fun doTest(path: String) { + val fileText = FileUtil.loadFile(File(path), true) + val intentionText = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INTENTION_TEXT: ") ?: error("Empty intention text") + val mainInspectionClassName = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty inspection class name") + val dependency = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// DEPENDENCY: ") + val intentionAvailable = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// INTENTION_NOT_AVAILABLE") + + val inspection = Class.forName(mainInspectionClassName).newInstance() as InspectionProfileEntry + myFixture.enableInspections(inspection) + + val sourceFile = myFixture.copyFileToProject(path, "src/${PathUtil.getFileName(path)}") + myFixture.configureFromExistingVirtualFile(sourceFile) + + if (dependency != null) { + val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim) + myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath") + } + + if (intentionAvailable) { + val intention = myFixture.getAvailableIntention(intentionText) ?: error("Failed to find intention") + myFixture.launchAction(intention) + myFixture.checkResultByFile(path + ".expected") + } + else { + assertNull("Intention should not be available", myFixture.availableIntentions.find { it.text == intentionText }) + } + } +} \ No newline at end of file diff --git a/idea/idea-gradle/build.gradle.kts b/idea/idea-gradle/build.gradle.kts index 3dd687b9fe0..8a5a8721140 100644 --- a/idea/idea-gradle/build.gradle.kts +++ b/idea/idea-gradle/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { testRuntime(intellijPluginDep("coverage")) testRuntime(intellijPluginDep("maven")) testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) } sourceSets { diff --git a/idea/idea-gradle/build.gradle.kts.173 b/idea/idea-gradle/build.gradle.kts.173 new file mode 100644 index 00000000000..3dd687b9fe0 --- /dev/null +++ b/idea/idea-gradle/build.gradle.kts.173 @@ -0,0 +1,66 @@ +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")) +} + +sourceSets { + "main" { + projectDefault() + resources.srcDir("res").apply { include("**") } + } + "test" { projectDefault() } +} + +testsJar() + +projectTest { + workingDir = rootDir + useAndroidSdk() +} + +configureInstrumentation() diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt index 8c34aef9dde..174f64dd332 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt @@ -41,7 +41,7 @@ object ThreadTrackerPatcherForTeamCityTesting { patched.compareAndSet(false, true) - IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool() + IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(true) // Check setup was successful and patching isn't needed val commonPoolFactoryName = ForkJoinPool.commonPool().factory::class.java.name diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt.173 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt.173 new file mode 100644 index 00000000000..8c34aef9dde --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/ThreadTrackerPatcherForTeamCityTesting.kt.173 @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2016 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 + +import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory +import com.intellij.testFramework.ThreadTracker +import java.lang.reflect.Modifier +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.atomic.AtomicBoolean + +/* +Workaround for ThreadTracker.checkLeak() failures on TeamCity. + +Currently TeamCity runs tests in classloader without boot.jar where IdeaForkJoinWorkerThreadFactory is defined. That +makes ForkJoinPool.commonPool() silently ignores java.util.concurrent.ForkJoinPool.common.threadFactory +(because of java.lang.ClassNotFoundException) option during factory initialization. + +Standard names for ForkJoinPool threads doesn't pass ThreadTracker.checkLeak() check and that ruins tests constantly. +As it's allowed to reorder tests on TeamCity and any test can be first at some point, this patch should be applied at +some common place. + */ +object ThreadTrackerPatcherForTeamCityTesting { + private val patched = AtomicBoolean(false) + + fun patchThreadTracker() { + if (patched.get()) return + + patched.compareAndSet(false, true) + + IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool() + + // Check setup was successful and patching isn't needed + val commonPoolFactoryName = ForkJoinPool.commonPool().factory::class.java.name + if (commonPoolFactoryName == IdeaForkJoinWorkerThreadFactory::class.java.name) { + return + } + + try { + val wellKnownOffendersField = try { + ThreadTracker::class.java.getDeclaredField("wellKnownOffenders") + } + catch (communityPropertyNotFoundEx: NoSuchFieldException) { + ThreadTracker::class.java.declaredFields.single { + Modifier.isStatic(it.modifiers) && MutableSet::class.java.isAssignableFrom(it.type) + } + } + + wellKnownOffendersField.isAccessible = true + + @Suppress("UNCHECKED_CAST") + val wellKnownOffenders = wellKnownOffendersField.get(null) as MutableSet + + wellKnownOffenders.add("ForkJoinPool.commonPool-worker-") + println("Patching ThreadTracker was successful") + } + catch (e: NoSuchFieldException) { + println("Patching ThreadTracker failed: " + e) + } + catch (e: IllegalAccessException) { + println("Patching ThreadTracker failed: " + e) + } + } +} \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt index 2da0e9f679a..ca5269494ed 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt @@ -50,7 +50,7 @@ class KotlinDebuggerSettings : XDebuggerSettings("kotlin override fun getState() = this override fun get() = this - override fun loadState(state: KotlinDebuggerSettings?) { - if (state != null) XmlSerializerUtil.copyBean(state, this) + override fun loadState(state: KotlinDebuggerSettings) { + XmlSerializerUtil.copyBean(state, this) } } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt.173 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt.173 new file mode 100644 index 00000000000..2da0e9f679a --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt.173 @@ -0,0 +1,56 @@ +/* + * 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.debugger + + +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.options.Configurable +import com.intellij.openapi.options.SimpleConfigurable +import com.intellij.openapi.util.Getter +import com.intellij.util.xmlb.XmlSerializerUtil +import com.intellij.xdebugger.XDebuggerUtil +import com.intellij.xdebugger.settings.DebuggerSettingsCategory +import com.intellij.xdebugger.settings.XDebuggerSettings +import org.jetbrains.kotlin.idea.debugger.stepping.KotlinSteppingConfigurableUi + +@State(name = "KotlinDebuggerSettings", storages = arrayOf(Storage("kotlin_debug.xml"))) +class KotlinDebuggerSettings : XDebuggerSettings("kotlin_debugger"), Getter { + var DEBUG_RENDER_DELEGATED_PROPERTIES: Boolean = true + var DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES: Boolean = true + var DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED: Boolean = false + + companion object { + fun getInstance(): KotlinDebuggerSettings { + return XDebuggerUtil.getInstance()?.getDebuggerSettings(KotlinDebuggerSettings::class.java)!! + } + } + + override fun createConfigurables(category: DebuggerSettingsCategory): Collection { + return when (category) { + DebuggerSettingsCategory.STEPPING -> + listOf(SimpleConfigurable.create( + "reference.idesettings.debugger.kotlin.stepping", + "Kotlin", + KotlinSteppingConfigurableUi::class.java, + this)) + DebuggerSettingsCategory.DATA_VIEWS -> + listOf(SimpleConfigurable.create( + "reference.idesettings.debugger.kotlin.data.view", + "Kotlin", + KotlinDelegatedPropertyRendererConfigurableUi::class.java, + this)) + else -> listOf() + } + } + + override fun getState() = this + override fun get() = this + + override fun loadState(state: KotlinDebuggerSettings?) { + if (state != null) XmlSerializerUtil.copyBean(state, this) + } +} diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt index a9489dd3fe4..81dd87c3f28 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt @@ -93,7 +93,7 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() { semaphore.down() val nameRef = AtomicReference() val worker = object : KotlinRuntimeTypeEvaluator( - null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator + null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator!! ) { override fun typeCalculationFinished(type: KotlinType?) { nameRef.set(type) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt.173 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt.173 new file mode 100644 index 00000000000..a9489dd3fe4 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt.173 @@ -0,0 +1,434 @@ +/* + * Copyright 2010-2016 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.debugger.evaluate + +import com.intellij.debugger.DebuggerManagerEx +import com.intellij.debugger.engine.evaluation.CodeFragmentFactory +import com.intellij.debugger.engine.evaluation.CodeFragmentKind +import com.intellij.debugger.engine.evaluation.TextWithImports +import com.intellij.debugger.engine.events.DebuggerCommandImpl +import com.intellij.debugger.impl.DebuggerContextImpl +import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.PsiTypesUtil +import com.intellij.util.IncorrectOperationException +import com.intellij.util.concurrency.Semaphore +import com.intellij.xdebugger.XDebuggerManager +import com.intellij.xdebugger.impl.XDebugSessionImpl +import com.intellij.xdebugger.impl.ui.tree.ValueMarkup +import com.sun.jdi.* +import org.jetbrains.annotations.TestOnly +import org.jetbrains.eval4j.jdi.asValue +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils +import org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider +import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor +import org.jetbrains.kotlin.idea.refactoring.j2k +import org.jetbrains.kotlin.idea.refactoring.j2kText +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass +import org.jetbrains.kotlin.j2k.AfterConversionPass +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.makeNullable +import java.util.concurrent.atomic.AtomicReference + +class KotlinCodeFragmentFactory : CodeFragmentFactory() { + override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { + val contextElement = getWrappedContextElement(project, context) + if (contextElement == null) { + LOG.warn("CodeFragment with null context created:\noriginalContext = ${context?.getElementTextWithContext()}") + } + val codeFragment = if (item.kind == CodeFragmentKind.EXPRESSION) { + KtExpressionCodeFragment( + project, + "fragment.kt", + item.text, + initImports(item.imports), + contextElement + ) + } else { + KtBlockCodeFragment( + project, + "fragment.kt", + item.text, + initImports(item.imports), + contextElement + ) + } + + codeFragment.putCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR, { expression: KtExpression -> + val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context + val debuggerSession = debuggerContext.debuggerSession + if (debuggerSession == null || debuggerContext.suspendContext == null) { + null + } else { + val semaphore = Semaphore() + semaphore.down() + val nameRef = AtomicReference() + val worker = object : KotlinRuntimeTypeEvaluator( + null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator + ) { + override fun typeCalculationFinished(type: KotlinType?) { + nameRef.set(type) + semaphore.up() + } + } + + debuggerContext.debugProcess?.managerThread?.invoke(worker) + + for (i in 0..50) { + ProgressManager.checkCanceled() + if (semaphore.waitFor(20)) break + } + + nameRef.get() + } + }) + + if (contextElement != null && contextElement !is KtElement) { + codeFragment.putCopyableUserData(KtCodeFragment.FAKE_CONTEXT_FOR_JAVA_FILE, { + val emptyFile = createFakeFileWithJavaContextElement("", contextElement) + + val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context + val debuggerSession = debuggerContext.debuggerSession + if ((debuggerSession == null || debuggerContext.suspendContext == null) && !ApplicationManager.getApplication().isUnitTestMode) { + LOG.warn("Couldn't create fake context element for java file, debugger isn't paused on breakpoint") + return@putCopyableUserData emptyFile + } + + val frameDescriptor = getFrameInfo(contextElement, debuggerContext) + if (frameDescriptor == null) { + LOG.warn("Couldn't get info about 'this' and local variables for ${debuggerContext.sourcePosition.file.name}:${debuggerContext.sourcePosition.line}") + return@putCopyableUserData emptyFile + } + + val receiverTypeReference = + frameDescriptor.thisObject?.let { createKotlinProperty(project, "this_0", it.type().name(), it) }?.typeReference + val receiverTypeText = receiverTypeReference?.let { "${it.text}." } ?: "" + + val kotlinVariablesText = + frameDescriptor.visibleVariables.entries.associate { it.key.name() to it.value }.kotlinVariablesAsText(project) + + val fakeFunctionText = "fun ${receiverTypeText}_java_locals_debug_fun_() {\n$kotlinVariablesText\n}" + + val fakeFile = createFakeFileWithJavaContextElement(fakeFunctionText, contextElement) + val fakeFunction = fakeFile.declarations.firstOrNull() as? KtFunction + val fakeContext = (fakeFunction?.bodyExpression as? KtBlockExpression)?.statements?.lastOrNull() + + return@putCopyableUserData wrapContextIfNeeded(project, contextElement, fakeContext) ?: emptyFile + }) + } + + return codeFragment + } + + private fun getFrameInfo(contextElement: PsiElement?, debuggerContext: DebuggerContextImpl): FrameInfo? { + val semaphore = Semaphore() + semaphore.down() + + var frameInfo: FrameInfo? = null + + val worker = object : DebuggerCommandImpl() { + override fun action() { + try { + val frame = if (ApplicationManager.getApplication().isUnitTestMode) + contextElement?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.frameProxy?.stackFrame + else + debuggerContext.frameProxy?.stackFrame + + val visibleVariables = frame?.let { + val values = it.getValues(it.visibleVariables()) + values.filterValues { it != null } + } ?: emptyMap() + + frameInfo = FrameInfo(frame?.thisObject(), visibleVariables) + } catch (ignored: AbsentInformationException) { + // Debug info unavailable + } finally { + semaphore.up() + } + } + } + + debuggerContext.debugProcess?.managerThread?.invoke(worker) + + for (i in 0..50) { + if (semaphore.waitFor(20)) break + } + + return frameInfo + } + + private class FrameInfo(val thisObject: Value?, val visibleVariables: Map) + + private fun initImports(imports: String?): String? { + if (imports != null && !imports.isEmpty()) { + return imports.split(KtCodeFragment.IMPORT_SEPARATOR) + .mapNotNull { fixImportIfNeeded(it) } + .joinToString(KtCodeFragment.IMPORT_SEPARATOR) + } + return null + } + + private fun fixImportIfNeeded(import: String): String? { + // skip arrays + if (import.endsWith("[]")) { + return fixImportIfNeeded(import.removeSuffix("[]").trim()) + } + + // skip primitive types + if (PsiTypesUtil.boxIfPossible(import) != import) { + return null + } + return import + } + + private fun getWrappedContextElement(project: Project, context: PsiElement?): PsiElement? { + val newContext = getContextElement(context) + if (newContext !is KtElement) return newContext + return wrapContextIfNeeded(project, context, newContext) + } + + override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { + val kotlinCodeFragment = createCodeFragment(item, context, project) + if (PsiTreeUtil.hasErrorElements(kotlinCodeFragment) && kotlinCodeFragment is KtExpressionCodeFragment) { + val javaExpression = try { + PsiElementFactory.SERVICE.getInstance(project).createExpressionFromText(item.text, context) + } catch (e: IncorrectOperationException) { + null + } + + val importList = try { + kotlinCodeFragment.importsAsImportList()?.let { + (PsiFileFactory.getInstance(project).createFileFromText( + "dummy.java", JavaFileType.INSTANCE, it.text + ) as? PsiJavaFile)?.importList + } + } catch (e: IncorrectOperationException) { + null + } + + if (javaExpression != null && !PsiTreeUtil.hasErrorElements(javaExpression)) { + var convertedFragment: KtExpressionCodeFragment? = null + project.executeWriteCommand("Convert java expression to kotlin in Evaluate Expression") { + try { + val newText = javaExpression.j2kText() + val newImports = importList?.j2kText() + if (newText != null) { + convertedFragment = KtExpressionCodeFragment( + project, + kotlinCodeFragment.name, + newText, + newImports, + kotlinCodeFragment.context + ) + + AfterConversionPass(project, J2kPostProcessor(formatCode = false)).run(convertedFragment!!, range = null) + } + } catch (e: Throwable) { + // ignored because text can be invalid + LOG.error("Couldn't convert expression:\n`${javaExpression.text}`", e) + } + } + return convertedFragment ?: kotlinCodeFragment + } + } + return kotlinCodeFragment + } + + override fun isContextAccepted(contextElement: PsiElement?): Boolean { + return when { + // PsiCodeBlock -> DummyHolder -> originalElement + contextElement is PsiCodeBlock -> isContextAccepted(contextElement.context?.context) + contextElement == null -> false + contextElement.language == KotlinFileType.INSTANCE.language -> true + contextElement.language == JavaFileType.INSTANCE.language -> { + getKotlinJvmRuntimeMarkerClass(contextElement.project, contextElement.resolveScope) != null + } + else -> false + } + } + + override fun getFileType(): KotlinFileType = KotlinFileType.INSTANCE + + override fun getEvaluatorBuilder() = KotlinEvaluationBuilder + + companion object { + private val LOG = Logger.getInstance(this::class.java) + + val LABEL_VARIABLE_VALUE_KEY: Key = Key.create("_label_variable_value_key_") + + private const val DEBUG_LABEL_SUFFIX: String = "_DebugLabel" + + @TestOnly + val DEBUG_CONTEXT_FOR_TESTS: Key = Key.create("DEBUG_CONTEXT_FOR_TESTS") + + fun getContextElement(elementAt: PsiElement?): PsiElement? { + if (elementAt == null) return null + + if (elementAt is PsiCodeBlock) { + return getContextElement(elementAt.context?.context) + } + + if (elementAt is KtLightClass) { + return getContextElement(elementAt.kotlinOrigin) + } + + val containingFile = elementAt.containingFile + if (containingFile is PsiJavaFile) return elementAt + if (containingFile !is KtFile) return null + + // elementAt can be PsiWhiteSpace when codeFragment is created from line start offset (in case of first opening EE window) + val lineStartOffset = if (elementAt is PsiWhiteSpace || elementAt is PsiComment) { + PsiTreeUtil.skipSiblingsForward(elementAt, PsiWhiteSpace::class.java, PsiComment::class.java)?.textOffset + ?: elementAt.textOffset + } else { + elementAt.textOffset + } + + fun KtElement.takeIfAcceptedAsCodeFragmentContext() = takeIf { KotlinEditorTextProvider.isAcceptedAsCodeFragmentContext(it) } + + PsiTreeUtil.findElementOfClassAtOffset(containingFile, lineStartOffset, KtExpression::class.java, false) + ?.takeIfAcceptedAsCodeFragmentContext() + ?.let { return CodeInsightUtils.getTopmostElementAtOffset(it, lineStartOffset, KtExpression::class.java) } + + KotlinEditorTextProvider.findExpressionInner(elementAt, true) + ?.takeIfAcceptedAsCodeFragmentContext() + ?.let { return it } + + return containingFile + } + + //internal for tests + fun createCodeFragmentForLabeledObjects(project: Project, markupMap: Map<*, ValueMarkup>): Pair> { + @Suppress("UNCHECKED_CAST") + val variables = markupMap.entries.associate { + val (value, markup) = it + "${markup.text}$DEBUG_LABEL_SUFFIX" to value as? Value + }.filterValues { it != null } as Map + + return variables.kotlinVariablesAsText(project) to variables + } + + private fun Map.kotlinVariablesAsText(project: Project): String { + val sb = StringBuilder() + + val psiNameHelper = PsiNameHelper.getInstance(project) + for ((variableName, variableValue) in entries) { + if (!psiNameHelper.isIdentifier(variableName)) continue + + val variableTypeName = variableValue.type()?.name() ?: continue + + val kotlinProperty = createKotlinProperty(project, variableName, variableTypeName, variableValue) ?: continue + + sb.append("${kotlinProperty.text}\n") + } + + sb.append("val _debug_context_val = 1\n") + + return sb.toString() + } + + private fun createKotlinProperty(project: Project, variableName: String, variableTypeName: String, value: Value): KtProperty? { + val actualClassDescriptor = value.asValue().asmType.getClassDescriptor(GlobalSearchScope.allScope(project)) + if (actualClassDescriptor != null && actualClassDescriptor.defaultType.arguments.isEmpty()) { + val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(actualClassDescriptor.defaultType.makeNullable()) + return KtPsiFactory(project).createProperty(variableName.quoteIfNeeded(), renderedType, false) + } + + fun String.addArraySuffix() = if (value is ArrayReference) this + "[]" else this + + val className = variableTypeName.replace("$", ".").substringBefore("[]") + val classType = PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project)) + val type = (if (value !is PrimitiveValue && classType.resolve() == null) + CommonClassNames.JAVA_LANG_OBJECT + else + className).addArraySuffix() + + val field = PsiElementFactory.SERVICE.getInstance(project) + .createField(variableName, PsiType.getTypeByName(type, project, GlobalSearchScope.allScope(project))) + val ktField = field.j2k() as? KtProperty + ktField?.modifierList?.delete() + return ktField + } + } + + private fun wrapContextIfNeeded(project: Project, originalContext: PsiElement?, newContext: KtElement?): KtElement? { + val markupMap: Map<*, ValueMarkup>? = + if (ApplicationManager.getApplication().isUnitTestMode) + NodeDescriptorImpl.getMarkupMap(originalContext?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.debugProcess) + else + (XDebuggerManager.getInstance(project).currentSession as? XDebugSessionImpl)?.valueMarkers?.allMarkers + + if (markupMap == null || markupMap.isEmpty()) return newContext + + val (text, labels) = createCodeFragmentForLabeledObjects(project, markupMap) + if (text.isEmpty()) return newContext + + return createWrappingContext(text, labels, newContext, project) + } + + private fun createFakeFileWithJavaContextElement(funWithLocalVariables: String, javaContext: PsiElement): KtFile { + val javaFile = javaContext.containingFile as? PsiJavaFile + + val sb = StringBuilder() + + javaFile?.packageName?.takeUnless { it.isBlank() }?.let { + sb.append("package ").append(it.quoteIfNeeded()).append("\n") + } + + javaFile?.importList?.let { sb.append(it.text).append("\n") } + + sb.append(funWithLocalVariables) + + return KtPsiFactory(javaContext.project).createAnalyzableFile("fakeFileForJavaContextInDebugger.kt", sb.toString(), javaContext) + } + + // internal for test + private fun createWrappingContext( + newFragmentText: String, + labels: Map, + originalContext: KtElement?, + project: Project + ): KtElement? { + val codeFragment = KtPsiFactory(project).createBlockCodeFragment(newFragmentText, originalContext) + + codeFragment.accept(object : KtTreeVisitorVoid() { + override fun visitProperty(property: KtProperty) { + val reference = labels[property.name] + if (reference != null) { + property.putUserData(LABEL_VARIABLE_VALUE_KEY, reference) + } + } + }) + + return codeFragment.getContentElement().statements.lastOrNull() + } +} diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt index dfa68796bf1..bd2cc4c29b1 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt @@ -133,9 +133,13 @@ class KotlinLanguageInjector( if (parts.ranges.isEmpty()) return InjectorUtils.registerInjection(language, parts.ranges, file, registrar) - InjectorUtils.registerSupport(support, false, registrar) - InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, - if (parts.isUnparsable) java.lang.Boolean.TRUE else null) + InjectorUtils.registerSupport(support, false, ktHost, language) + InjectorUtils.putInjectedFileUserData( + ktHost, + language, + InjectedLanguageUtil.FRANKENSTEIN_INJECTION, + if (parts.isUnparsable) java.lang.Boolean.TRUE else null + ) } else { InjectorUtils.registerInjectionSimple(ktHost, baseInjection, support, registrar) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt.173 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt.173 new file mode 100644 index 00000000000..dfa68796bf1 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt.173 @@ -0,0 +1,413 @@ +/* + * Copyright 2010-2016 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.injection + +import com.intellij.codeInsight.AnnotationUtil +import com.intellij.lang.injection.MultiHostInjector +import com.intellij.lang.injection.MultiHostRegistrar +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.text.StringUtilRt +import com.intellij.patterns.PatternCondition +import com.intellij.patterns.PatternConditionPlus +import com.intellij.patterns.PsiClassNamePatternCondition +import com.intellij.patterns.ValuePatternCondition +import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil +import com.intellij.psi.search.LocalSearchScope +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import org.intellij.plugins.intelliLang.Configuration +import org.intellij.plugins.intelliLang.inject.InjectorUtils +import org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport +import org.intellij.plugins.intelliLang.inject.TemporaryPlacesRegistry +import org.intellij.plugins.intelliLang.inject.config.BaseInjection +import org.intellij.plugins.intelliLang.inject.config.InjectionPlace +import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport +import org.intellij.plugins.intelliLang.util.AnnotationUtilEx +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.patterns.KotlinFunctionPattern +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.runInReadActionWithWriteActionPriority +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.util.aliasImportMap +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +class KotlinLanguageInjector( + private val configuration: Configuration, + private val project: Project, + private val temporaryPlacesRegistry: TemporaryPlacesRegistry +) : MultiHostInjector { + companion object { + private val STRING_LITERALS_REGEXP = "\"([^\"]*)\"".toRegex() + private val ABSENT_KOTLIN_INJECTION = BaseInjection("ABSENT_KOTLIN_BASE_INJECTION") + } + + var annotationInjectionsEnabled = Registry.`is`("kotlin.annotation.injection.enabled", false) + + private val kotlinSupport: KotlinLanguageInjectionSupport? by lazy { + ArrayList(InjectorUtils.getActiveInjectionSupports()).filterIsInstance(KotlinLanguageInjectionSupport::class.java).firstOrNull() + } + + private data class KotlinCachedInjection(val modificationCount: Long, val baseInjection: BaseInjection) + + private var KtStringTemplateExpression.cachedInjectionWithModification: KotlinCachedInjection? by UserDataProperty( + Key.create("CACHED_INJECTION_WITH_MODIFICATION")) + + override fun getLanguagesToInject(registrar: MultiHostRegistrar, context: PsiElement) { + val ktHost: KtStringTemplateExpression = context as? KtStringTemplateExpression ?: return + if (!context.isValidHost) return + + val support = kotlinSupport ?: return + + if (!ProjectRootsUtil.isInProjectOrLibSource(ktHost.containingFile.originalFile)) return + + val needImmediateAnswer = with(ApplicationManager.getApplication()) { isDispatchThread && !isUnitTestMode } + val kotlinCachedInjection = ktHost.cachedInjectionWithModification + val modificationCount = PsiManager.getInstance(project).modificationTracker.modificationCount + + val baseInjection = when { + needImmediateAnswer -> { + // Can't afford long counting or typing will be laggy. Force cache reuse even if it's outdated. + kotlinCachedInjection?.baseInjection ?: ABSENT_KOTLIN_INJECTION + } + kotlinCachedInjection != null && (modificationCount == kotlinCachedInjection.modificationCount) -> + // Cache is up-to-date + kotlinCachedInjection.baseInjection + else -> { + fun computeAndCache(): BaseInjection { + val computedInjection = computeBaseInjection(ktHost, support, registrar) ?: ABSENT_KOTLIN_INJECTION + ktHost.cachedInjectionWithModification = KotlinCachedInjection(modificationCount, computedInjection) + return computedInjection + } + + if (ApplicationManager.getApplication().isReadAccessAllowed && ProgressManager.getInstance().progressIndicator == null) { + // The action cannot be canceled by caller and by internal checkCanceled() calls. + // Force creating new indicator that is canceled on write action start, otherwise there might be lags in typing. + runInReadActionWithWriteActionPriority(::computeAndCache) ?: kotlinCachedInjection?.baseInjection ?: ABSENT_KOTLIN_INJECTION + } + else { + computeAndCache() + } + } + } + + if (baseInjection == ABSENT_KOTLIN_INJECTION) { + return + } + + val language = InjectorUtils.getLanguageByString(baseInjection.injectedLanguageId) ?: return + + if (ktHost.hasInterpolation()) { + val file = ktHost.containingKtFile + val parts = splitLiteralToInjectionParts(baseInjection, ktHost) ?: return + + if (parts.ranges.isEmpty()) return + + InjectorUtils.registerInjection(language, parts.ranges, file, registrar) + InjectorUtils.registerSupport(support, false, registrar) + InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, + if (parts.isUnparsable) java.lang.Boolean.TRUE else null) + } + else { + InjectorUtils.registerInjectionSimple(ktHost, baseInjection, support, registrar) + } + } + + @Suppress("FoldInitializerAndIfToElvis") + private fun computeBaseInjection( + ktHost: KtStringTemplateExpression, + support: KotlinLanguageInjectionSupport, + registrar: MultiHostRegistrar): BaseInjection? { + val containingFile = ktHost.containingFile + + val tempInjectedLanguage = temporaryPlacesRegistry.getLanguageFor(ktHost, containingFile) + if (tempInjectedLanguage != null) { + InjectorUtils.putInjectedFileUserData(registrar, LanguageInjectionSupport.TEMPORARY_INJECTED_LANGUAGE, tempInjectedLanguage) + return BaseInjection(support.id).apply { + injectedLanguageId = tempInjectedLanguage.id + prefix = tempInjectedLanguage.prefix + suffix = tempInjectedLanguage.suffix + } + } + + return findInjectionInfo(ktHost)?.toBaseInjection(support) + } + + override fun elementsToInjectIn(): List> { + return listOf(KtStringTemplateExpression::class.java) + } + + private fun findInjectionInfo(place: KtElement, originalHost: Boolean = true): InjectionInfo? { + return injectWithExplicitCodeInstruction(place) + ?: injectWithCall(place) + ?: injectInAnnotationCall(place) + ?: injectWithReceiver(place) + ?: injectWithVariableUsage(place, originalHost) + } + + private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? { + val support = kotlinSupport ?: return null + return InjectionInfo.fromBaseInjection(support.findCommentInjection(host)) ?: support.findAnnotationInjectionLanguageId(host) + } + + private fun injectWithReceiver(host: KtElement): InjectionInfo? { + val qualifiedExpression = host.parent as? KtDotQualifiedExpression ?: return null + if (qualifiedExpression.receiverExpression != host) return null + + val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null + val callee = callExpression.calleeExpression ?: return null + + if (isAnalyzeOff()) return null + + val kotlinInjections = configuration.getInjections(KOTLIN_SUPPORT_ID) + + val calleeName = callee.text + val possibleNames = collectPossibleNames(kotlinInjections) + + if (calleeName !in possibleNames) { + return null + } + + for (reference in callee.references) { + ProgressManager.checkCanceled() + + val resolvedTo = reference.resolve() + if (resolvedTo is KtFunction) { + val injectionInfo = findInjection(resolvedTo.receiverTypeReference, kotlinInjections) + if (injectionInfo != null) { + return injectionInfo + } + } + } + + return null + } + + private fun collectPossibleNames(injections: List): Set { + val result = HashSet() + + for (injection in injections) { + val injectionPlaces = injection.injectionPlaces + for (place in injectionPlaces) { + val placeStr = place.toString() + val literals = STRING_LITERALS_REGEXP.findAll(placeStr).map { it.groupValues[1] } + result.addAll(literals) + } + } + + return result + } + + private fun injectWithVariableUsage(host: KtElement, originalHost: Boolean): InjectionInfo? { + // Given place is not original host of the injection so we stop to prevent stepping through indirect references + if (!originalHost) return null + + val ktProperty = host.parent as? KtProperty ?: return null + if (ktProperty.initializer != host) return null + + if (isAnalyzeOff()) return null + + val searchScope = LocalSearchScope(arrayOf(ktProperty.containingFile), "", true) + return ReferencesSearch.search(ktProperty, searchScope).asSequence().mapNotNull { psiReference -> + val element = psiReference.element as? KtElement ?: return@mapNotNull null + findInjectionInfo(element, false) + }.firstOrNull() + } + + private fun injectWithCall(host: KtElement): InjectionInfo? { + val ktHost: KtElement = host + val argument = ktHost.parent as? KtValueArgument ?: return null + + val callExpression = PsiTreeUtil.getParentOfType(ktHost, KtCallElement::class.java) ?: return null + val callee = getNameReference(callExpression.calleeExpression) ?: return null + + if (isAnalyzeOff()) return null + + for (reference in callee.references) { + ProgressManager.checkCanceled() + + val resolvedTo = reference.resolve() + if (resolvedTo is PsiMethod) { + val injectionForJavaMethod = injectionForJavaMethod(argument, resolvedTo) + if (injectionForJavaMethod != null) { + return injectionForJavaMethod + } + } + else if (resolvedTo is KtFunction) { + val injectionForJavaMethod = injectionForKotlinCall(argument, resolvedTo, reference) + if (injectionForJavaMethod != null) { + return injectionForJavaMethod + } + } + } + + return null + } + + private fun getNameReference(callee: KtExpression?): KtNameReferenceExpression? { + if (callee is KtConstructorCalleeExpression) + return callee.constructorReferenceExpression as? KtNameReferenceExpression + return callee as? KtNameReferenceExpression + } + + private fun injectInAnnotationCall(host: KtElement): InjectionInfo? { + if (!annotationInjectionsEnabled) return null + val argument = host.parent as? KtValueArgument ?: return null + val annotationEntry = argument.parent.parent as? KtCallElement ?: return null + if (!fastCheckInjectionsExists(annotationEntry)) return null + val calleeExpression = annotationEntry.calleeExpression ?: return null + val callee = getNameReference(calleeExpression)?.mainReference?.resolve() + when (callee) { + is PsiClass -> { + val psiClass = callee as? PsiClass ?: return null + val argumentName = argument.getArgumentName()?.asName?.identifier ?: "value" + val method = psiClass.findMethodsByName(argumentName, false).singleOrNull() ?: return null + return findInjection(method, configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) + } + else -> return null + } + + } + + private fun injectionForJavaMethod(argument: KtValueArgument, javaMethod: PsiMethod): InjectionInfo? { + val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument) + val psiParameter = javaMethod.parameterList.parameters.getOrNull(argumentIndex) ?: return null + + val injectionInfo = findInjection(psiParameter, configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) + if (injectionInfo != null) { + return injectionInfo + } + + val annotations = AnnotationUtilEx.getAnnotationFrom( + psiParameter, + configuration.advancedConfiguration.languageAnnotationPair, + true) + + if (annotations.isNotEmpty()) { + return processAnnotationInjectionInner(annotations) + } + + return null + } + + private fun injectionForKotlinCall(argument: KtValueArgument, ktFunction: KtFunction, reference: PsiReference): InjectionInfo? { + val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument) + val ktParameter = ktFunction.valueParameters.getOrNull(argumentIndex) ?: return null + + val patternInjection = findInjection(ktParameter, configuration.getInjections(KOTLIN_SUPPORT_ID)) + if (patternInjection != null) { + return patternInjection + } + + // Found psi element after resolve can be obtained from compiled declaration but annotations parameters are lost there. + // Search for original descriptor from reference. + val ktReference = reference as? KtReference ?: return null + val bindingContext = ktReference.element.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS) + val functionDescriptor = ktReference.resolveToDescriptors(bindingContext).singleOrNull() as? FunctionDescriptor ?: return null + + val parameterDescriptor = functionDescriptor.valueParameters.getOrNull(argumentIndex) ?: return null + val injectAnnotation = parameterDescriptor.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null + + val languageId = injectAnnotation.argumentValue("value")?.safeAs()?.value ?: return null + return InjectionInfo(languageId, null, null) + } + + private fun findInjection(element: PsiElement?, injections: List): InjectionInfo? { + for (injection in injections) { + if (injection.acceptsPsiElement(element)) { + return InjectionInfo(injection.injectedLanguageId, injection.prefix, injection.suffix) + } + } + + return null + } + + private fun isAnalyzeOff(): Boolean { + return configuration.advancedConfiguration.dfaOption == Configuration.DfaOption.OFF + } + + private fun processAnnotationInjectionInner(annotations: Array): InjectionInfo? { + val id = AnnotationUtilEx.calcAnnotationValue(annotations, "value") + val prefix = AnnotationUtilEx.calcAnnotationValue(annotations, "prefix") + val suffix = AnnotationUtilEx.calcAnnotationValue(annotations, "suffix") + + return InjectionInfo(id, prefix, suffix) + } + + private val injectableTargetClassShortNames = CachedValuesManager.getManager(project) + .createCachedValue({ + CachedValueProvider.Result.create(HashSet().apply { + for (injection in configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) { + for (injectionPlace in injection.injectionPlaces) { + for (targetClassFQN in retrieveJavaPlaceTargetClassesFQNs(injectionPlace)) { + add(StringUtilRt.getShortName(targetClassFQN)) + } + } + } + for (injection in configuration.getInjections(org.jetbrains.kotlin.idea.injection.KOTLIN_SUPPORT_ID)) { + for (injectionPlace in injection.injectionPlaces) { + for (targetClassFQN in retrieveKotlinPlaceTargetClassesFQNs(injectionPlace)) { + add(StringUtilRt.getShortName(targetClassFQN)) + } + } + } + }, configuration) + }, false) + + private fun fastCheckInjectionsExists(annotationEntry: KtCallElement): Boolean { + val referencedName = getNameReference(annotationEntry.calleeExpression)?.getReferencedName() ?: return false + val annotationShortName = annotationEntry.containingKtFile.aliasImportMap()[referencedName].singleOrNull() ?: referencedName + return annotationShortName in injectableTargetClassShortNames.value + } + + private fun retrieveJavaPlaceTargetClassesFQNs(place: InjectionPlace): Collection { + val classCondition = place.elementPattern.condition.conditions.firstOrNull { it.debugMethodName == "definedInClass" } + as? PatternConditionPlus<*, *> ?: return emptyList() + val psiClassNamePatternCondition = classCondition.valuePattern.condition.conditions. + firstIsInstanceOrNull() ?: return emptyList() + val valuePatternCondition = psiClassNamePatternCondition.namePattern.condition.conditions.firstIsInstanceOrNull>() ?: return emptyList() + return valuePatternCondition.values + } + + private fun retrieveKotlinPlaceTargetClassesFQNs(place: InjectionPlace): Collection { + val classNames = SmartList() + fun collect(condition: PatternCondition<*>) { + when (condition) { + is PatternConditionPlus<*, *> -> condition.valuePattern.condition.conditions.forEach { collect(it) } + is KotlinFunctionPattern.DefinedInClassCondition -> classNames.add(condition.fqName) + } + } + place.elementPattern.condition.conditions.forEach { collect(it) } + return classNames + } + +} \ No newline at end of file diff --git a/idea/idea-maven/build.gradle.kts b/idea/idea-maven/build.gradle.kts index d2bf342c539..2c1bc79c33b 100644 --- a/idea/idea-maven/build.gradle.kts +++ b/idea/idea-maven/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { testRuntime(intellijPluginDep("coverage")) testRuntime(intellijPluginDep("maven")) testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) } sourceSets { diff --git a/idea/idea-maven/build.gradle.kts.173 b/idea/idea-maven/build.gradle.kts.173 new file mode 100644 index 00000000000..d2bf342c539 --- /dev/null +++ b/idea/idea-maven/build.gradle.kts.173 @@ -0,0 +1,61 @@ + +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")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +testsJar() + +projectTest { + workingDir = rootDir +} diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt index f2b1fd00f76..e7c9f67481f 100644 --- a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt @@ -373,18 +373,16 @@ private enum class SourceType { @State( name = "AutoImportedSourceRoots", - storages = [(Storage(id = "other", file = StoragePathMacros.MODULE_FILE))] + storages = [(Storage(file = StoragePathMacros.MODULE_FILE))] ) class KotlinImporterComponent : PersistentStateComponent { class State(var directories: List = ArrayList()) val addedSources: MutableSet = Collections.synchronizedSet(HashSet()) - override fun loadState(state: State?) { + override fun loadState(state: State) { addedSources.clear() - if (state != null) { - addedSources.addAll(state.directories) - } + addedSources.addAll(state.directories) } override fun getState(): State { diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt.173 b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt.173 new file mode 100644 index 00000000000..f2b1fd00f76 --- /dev/null +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt.173 @@ -0,0 +1,396 @@ +/* + * 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.maven + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.StoragePathMacros +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.CompilerModuleExtension +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.openapi.roots.OrderEnumerator +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.impl.libraries.LibraryEx +import com.intellij.openapi.roots.libraries.Library +import com.intellij.openapi.util.AsyncResult +import com.intellij.util.PathUtil +import org.jdom.Element +import org.jdom.Text +import org.jetbrains.idea.maven.importing.MavenImporter +import org.jetbrains.idea.maven.importing.MavenRootModelAdapter +import org.jetbrains.idea.maven.model.MavenPlugin +import org.jetbrains.idea.maven.project.* +import org.jetbrains.jps.model.java.JavaSourceRootType +import org.jetbrains.jps.model.module.JpsModuleSourceRootType +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.cli.common.arguments.parseCommandLineArguments +import org.jetbrains.kotlin.compilerRunner.ArgumentUtils +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.idea.facet.* +import org.jetbrains.kotlin.idea.framework.detectLibraryKind +import org.jetbrains.kotlin.idea.framework.libraryKind +import org.jetbrains.kotlin.idea.maven.configuration.KotlinMavenConfigurator +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addIfNotNull +import java.io.File +import java.util.* + +interface MavenProjectImportHandler { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.mavenProjectImportHandler", + MavenProjectImportHandler::class.java + ) + + operator fun invoke(facet: KotlinFacet, mavenProject: MavenProject) +} + +class KotlinMavenImporter : MavenImporter(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID) { + companion object { + const val KOTLIN_PLUGIN_GROUP_ID = "org.jetbrains.kotlin" + const val KOTLIN_PLUGIN_ARTIFACT_ID = "kotlin-maven-plugin" + + const val KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG = "sourceDirs" + } + + override fun preProcess( + module: Module, + mavenProject: MavenProject, + changes: MavenProjectChanges, + modifiableModelsProvider: IdeModifiableModelsProvider + ) { + } + + override fun process( + modifiableModelsProvider: IdeModifiableModelsProvider, + module: Module, + rootModel: MavenRootModelAdapter, + mavenModel: MavenProjectsTree, + mavenProject: MavenProject, + changes: MavenProjectChanges, + mavenProjectToModuleName: MutableMap, + postTasks: MutableList + ) { + + if (changes.plugins) { + contributeSourceDirectories(mavenProject, module, rootModel) + } + } + + override fun postProcess( + module: Module, + mavenProject: MavenProject, + changes: MavenProjectChanges, + modifiableModelsProvider: IdeModifiableModelsProvider + ) { + super.postProcess(module, mavenProject, changes, modifiableModelsProvider) + + if (changes.dependencies) { + scheduleDownloadStdlibSources(mavenProject, module) + + val targetLibraryKind = detectPlatformByExecutions(mavenProject)?.libraryKind + if (targetLibraryKind != null) { + modifiableModelsProvider.getModifiableRootModel(module).orderEntries().forEachLibrary { library -> + if ((library as LibraryEx).kind == null) { + val model = modifiableModelsProvider.getModifiableLibraryModel(library) as LibraryEx.ModifiableModelEx + detectLibraryKind(model.getFiles(OrderRootType.CLASSES))?.let { model.kind = it } + } + true + } + } + } + + configureFacet(mavenProject, modifiableModelsProvider, module) + } + + private fun scheduleDownloadStdlibSources(mavenProject: MavenProject, module: Module) { + // TODO: here we have to process all kotlin libraries but for now we only handle standard libraries + val artifacts = mavenProject.dependencyArtifactIndex.data[KOTLIN_PLUGIN_GROUP_ID]?.values?.flatMap { it.filter { it.isResolved } } + ?: emptyList() + + val librariesWithNoSources = ArrayList() + OrderEnumerator.orderEntries(module).forEachLibrary { library -> + if (library.modifiableModel.getFiles(OrderRootType.SOURCES).isEmpty()) { + librariesWithNoSources.add(library) + } + true + } + val libraryNames = librariesWithNoSources.mapTo(HashSet()) { it.name } + val toBeDownloaded = artifacts.filter { it.libraryName in libraryNames } + + if (toBeDownloaded.isNotEmpty()) { + MavenProjectsManager.getInstance(module.project) + .scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncResult()) + } + } + + private fun configureJSOutputPaths( + mavenProject: MavenProject, + modifiableRootModel: ModifiableRootModel, + facetSettings: KotlinFacetSettings, + mavenPlugin: MavenPlugin + ) { + fun parentPath(path: String): String = + File(path).absoluteFile.parentFile.absolutePath + + val sharedOutputFile = mavenPlugin.configurationElement?.getChild("outputFile")?.text + + val compilerModuleExtension = modifiableRootModel.getModuleExtension(CompilerModuleExtension::class.java) ?: return + val buildDirectory = mavenProject.buildDirectory + + val executions = mavenPlugin.executions + + executions.forEach { + val explicitOutputFile = it.configurationElement?.getChild("outputFile")?.text ?: sharedOutputFile + if (PomFile.KotlinGoals.Js in it.goals) { + // see org.jetbrains.kotlin.maven.K2JSCompilerMojo + val outputFilePath = PathUtil.toSystemDependentName(explicitOutputFile ?: "$buildDirectory/js/${mavenProject.mavenId.artifactId}.js") + compilerModuleExtension.setCompilerOutputPath(parentPath(outputFilePath)) + facetSettings.productionOutputPath = outputFilePath + } + if (PomFile.KotlinGoals.TestJs in it.goals) { + // see org.jetbrains.kotlin.maven.KotlinTestJSCompilerMojo + val outputFilePath = PathUtil.toSystemDependentName(explicitOutputFile ?: "$buildDirectory/test-js/${mavenProject.mavenId.artifactId}-tests.js") + compilerModuleExtension.setCompilerOutputPathForTests(parentPath(outputFilePath)) + facetSettings.testOutputPath = outputFilePath + } + } + } + + private fun getCompilerArgumentsByConfigurationElement( + mavenProject: MavenProject, + configuration: Element?, + platform: TargetPlatformKind<*> + ): List { + val arguments = when (platform) { + is TargetPlatformKind.Jvm -> K2JVMCompilerArguments() + TargetPlatformKind.JavaScript -> K2JSCompilerArguments() + TargetPlatformKind.Common -> K2MetadataCompilerArguments() + } + + arguments.apiVersion = configuration?.getChild("apiVersion")?.text ?: + mavenProject.properties["kotlin.compiler.apiVersion"]?.toString() + arguments.languageVersion = configuration?.getChild("languageVersion")?.text ?: + mavenProject.properties["kotlin.compiler.languageVersion"]?.toString() + arguments.multiPlatform = configuration?.getChild("multiPlatform")?.text?.trim()?.toBoolean() ?: false + arguments.suppressWarnings = configuration?.getChild("nowarn")?.text?.trim()?.toBoolean() ?: false + + configuration?.getChild("experimentalCoroutines")?.text?.trim()?.let { + arguments.coroutinesState = it + } + + when (arguments) { + is K2JVMCompilerArguments -> { + arguments.classpath = configuration?.getChild("classpath")?.text + arguments.jdkHome = configuration?.getChild("jdkHome")?.text + arguments.jvmTarget = configuration?.getChild("jvmTarget")?.text ?: + mavenProject.properties["kotlin.compiler.jvmTarget"]?.toString() + arguments.javaParameters = configuration?.getChild("javaParameters")?.text?.toBoolean() ?: false + } + is K2JSCompilerArguments -> { + arguments.sourceMap = configuration?.getChild("sourceMap")?.text?.trim()?.toBoolean() ?: false + arguments.sourceMapPrefix = configuration?.getChild("sourceMapPrefix")?.text?.trim() ?: "" + arguments.sourceMapEmbedSources = configuration?.getChild("sourceMapEmbedSources")?.text?.trim() ?: "inlining" + arguments.outputFile = configuration?.getChild("outputFile")?.text + arguments.metaInfo = configuration?.getChild("metaInfo")?.text?.trim()?.toBoolean() ?: false + arguments.moduleKind = configuration?.getChild("moduleKind")?.text + arguments.main = configuration?.getChild("main")?.text + } + } + + val additionalArgs = SmartList().apply { + val argsElement = configuration?.getChild("args") + + argsElement?.content?.forEach { argElement -> + when (argElement) { + is Text -> { + argElement.text?.let { addAll(splitArgumentString(it)) } + } + is Element -> { + if (argElement.name == "arg") { + addIfNotNull(argElement.text) + } + } + } + } + } + parseCommandLineArguments(additionalArgs, arguments) + + return ArgumentUtils.convertArgumentsToStringList(arguments) + } + + private val compilationGoals = listOf( + PomFile.KotlinGoals.Compile, + PomFile.KotlinGoals.TestCompile, + PomFile.KotlinGoals.Js, + PomFile.KotlinGoals.TestJs, + PomFile.KotlinGoals.MetaData + ) + + private fun configureFacet(mavenProject: MavenProject, modifiableModelsProvider: IdeModifiableModelsProvider, module: Module) { + val mavenPlugin = mavenProject.findPlugin(KotlinMavenConfigurator.GROUP_ID, KotlinMavenConfigurator.MAVEN_PLUGIN_ID) ?: return + val compilerVersion = mavenPlugin.version ?: LanguageVersion.LATEST_STABLE.versionString + val kotlinFacet = module.getOrCreateFacet(modifiableModelsProvider, false) + val platform = detectPlatform(mavenProject) + + kotlinFacet.configureFacet(compilerVersion, LanguageFeature.Coroutines.defaultState, platform, modifiableModelsProvider) + val facetSettings = kotlinFacet.configuration.settings + val configuredPlatform = kotlinFacet.configuration.settings.targetPlatformKind!! + val configuration = mavenPlugin.configurationElement + val sharedArguments = getCompilerArgumentsByConfigurationElement(mavenProject, configuration, configuredPlatform) + val executionArguments = mavenPlugin.executions + ?.firstOrNull { it.goals.any { it in compilationGoals } } + ?.configurationElement?.let { getCompilerArgumentsByConfigurationElement(mavenProject, it, configuredPlatform) } + parseCompilerArgumentsToFacet(sharedArguments, emptyList(), kotlinFacet, modifiableModelsProvider) + if (executionArguments != null) { + parseCompilerArgumentsToFacet(executionArguments, emptyList(), kotlinFacet, modifiableModelsProvider) + } + if (facetSettings.compilerArguments is K2JSCompilerArguments) { + configureJSOutputPaths(mavenProject, modifiableModelsProvider.getModifiableRootModel(module), facetSettings, mavenPlugin) + } + MavenProjectImportHandler.getInstances(module.project).forEach { it(kotlinFacet, mavenProject) } + setImplementedModuleName(kotlinFacet, mavenProject, module) + kotlinFacet.noVersionAutoAdvance() + } + + private fun detectPlatform(mavenProject: MavenProject) = + detectPlatformByExecutions(mavenProject) ?: detectPlatformByLibraries(mavenProject) + + private fun detectPlatformByExecutions(mavenProject: MavenProject): TargetPlatformKind<*>? { + return mavenProject.findPlugin(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID)?.executions?.flatMap { it.goals } + ?.mapNotNull { goal -> + when (goal) { + PomFile.KotlinGoals.Compile, PomFile.KotlinGoals.TestCompile -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6] + PomFile.KotlinGoals.Js, PomFile.KotlinGoals.TestJs -> TargetPlatformKind.JavaScript + PomFile.KotlinGoals.MetaData -> TargetPlatformKind.Common + else -> null + } + }?.distinct()?.singleOrNull() + } + + private fun detectPlatformByLibraries(mavenProject: MavenProject): TargetPlatformKind<*>? { + return TargetPlatformKind.ALL_PLATFORMS.firstOrNull { + it.mavenLibraryIds.any { mavenProject.findDependencies(KOTLIN_PLUGIN_GROUP_ID, it).isNotEmpty() } + } + } + + // TODO in theory it should work like this but it doesn't as it couldn't unmark source roots that are not roots anymore. + // I believe this is something should be done by the underlying maven importer implementation or somewhere else in the IDEA + // For now there is a contributeSourceDirectories implementation that deals with the issue + // see https://youtrack.jetbrains.com/issue/IDEA-148280 + + // override fun collectSourceRoots(mavenProject: MavenProject, result: PairConsumer>) { + // for ((type, dir) in collectSourceDirectories(mavenProject)) { + // val jpsType: JpsModuleSourceRootType<*> = when (type) { + // SourceType.PROD -> JavaSourceRootType.SOURCE + // SourceType.TEST -> JavaSourceRootType.TEST_SOURCE + // } + // + // result.consume(dir, jpsType) + // } + // } + + private fun contributeSourceDirectories(mavenProject: MavenProject, module: Module, rootModel: MavenRootModelAdapter) { + val directories = collectSourceDirectories(mavenProject) + + val toBeAdded = directories.map { it.second }.toSet() + val state = module.kotlinImporterComponent + + for ((type, dir) in directories) { + if (rootModel.getSourceFolder(File(dir)) == null) { + val jpsType: JpsModuleSourceRootType<*> = when (type) { + SourceType.TEST -> JavaSourceRootType.TEST_SOURCE + SourceType.PROD -> JavaSourceRootType.SOURCE + } + + rootModel.addSourceFolder(dir, jpsType) + } + } + + state.addedSources.filter { it !in toBeAdded }.forEach { + rootModel.unregisterAll(it, true, true) + state.addedSources.remove(it) + } + state.addedSources.addAll(toBeAdded) + } + + private fun collectSourceDirectories(mavenProject: MavenProject): List> = + mavenProject.plugins.filter { it.isKotlinPlugin() }.flatMap { plugin -> + plugin.configurationElement.sourceDirectories().map { SourceType.PROD to it } + + plugin.executions.flatMap { execution -> + execution.configurationElement.sourceDirectories().map { execution.sourceType() to it } + } + }.distinct() + + private fun setImplementedModuleName(kotlinFacet: KotlinFacet, mavenProject: MavenProject, module: Module) { + if (kotlinFacet.configuration.settings.targetPlatformKind == TargetPlatformKind.Common) { + kotlinFacet.configuration.settings.implementedModuleNames = emptyList() + } else { + val manager = MavenProjectsManager.getInstance(module.project) + val mavenDependencies = mavenProject.dependencies.mapNotNull { manager?.findProject(it) } + val implemented = mavenDependencies.filter { detectPlatformByExecutions(it) == TargetPlatformKind.Common } + + kotlinFacet.configuration.settings.implementedModuleNames = implemented.map { manager.findModule(it)?.name ?: it.displayName } + } + } +} + +private fun MavenPlugin.isKotlinPlugin() = + groupId == KotlinMavenImporter.KOTLIN_PLUGIN_GROUP_ID && artifactId == KotlinMavenImporter.KOTLIN_PLUGIN_ARTIFACT_ID + +private fun Element?.sourceDirectories(): List = + this?.getChildren(KotlinMavenImporter.KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG)?.flatMap { it.children ?: emptyList() }?.map { it.textTrim } + ?: emptyList() + +private fun MavenPlugin.Execution.sourceType() = + goals.map { if (isTestGoalName(it)) SourceType.TEST else SourceType.PROD } + .distinct() + .singleOrNull() ?: SourceType.PROD + +private fun isTestGoalName(goalName: String) = goalName.startsWith("test-") + +private enum class SourceType { + PROD, TEST +} + +@State( + name = "AutoImportedSourceRoots", + storages = [(Storage(id = "other", file = StoragePathMacros.MODULE_FILE))] +) +class KotlinImporterComponent : PersistentStateComponent { + class State(var directories: List = ArrayList()) + + val addedSources: MutableSet = Collections.synchronizedSet(HashSet()) + + override fun loadState(state: State?) { + addedSources.clear() + if (state != null) { + addedSources.addAll(state.directories) + } + } + + override fun getState(): State { + return State(addedSources.sorted()) + } +} + +private val Module.kotlinImporterComponent: KotlinImporterComponent + get() = getComponent(KotlinImporterComponent::class.java) ?: throw IllegalStateException("No maven importer state configured") diff --git a/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after b/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after index 0236b164547..46a601c7653 100644 --- a/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after +++ b/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after @@ -26,6 +26,7 @@ org.jetbrains.kotlin kotlin-reflect RELEASE + compile diff --git a/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after.173 b/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after.173 new file mode 100644 index 00000000000..0236b164547 --- /dev/null +++ b/idea/idea-maven/testData/languageFeature/addKotlinReflect/pom.xml.after.173 @@ -0,0 +1,61 @@ + + + 4.0.0 + + maventest + maventest + 1.0-SNAPSHOT + + $VERSION$ + + + + org.jetbrains.kotlin + kotlin-stdlib-jre8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + org.jetbrains.kotlin + kotlin-reflect + RELEASE + + + + + ${project.basedir}/src/main/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + wrong-goal + compile + + + + test-compile + test-compile + + test-compile + + + + + + + + + \ No newline at end of file diff --git a/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt b/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt index b485be5d25e..e3c05bcae1b 100644 --- a/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt +++ b/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt @@ -69,7 +69,6 @@ abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFix } }) } - CodeInsightTestCase.fixTemplates() } override fun tearDown() { diff --git a/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt.173 b/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt.173 new file mode 100644 index 00000000000..b485be5d25e --- /dev/null +++ b/idea/idea-test-framework/test/org/jetbrains/kotlin/idea/test/KotlinLightCodeInsightFixtureTestCase.kt.173 @@ -0,0 +1,231 @@ +/* + * 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.test + +import com.intellij.codeInsight.CodeInsightTestCase +import com.intellij.codeInsight.daemon.impl.EditorTracker +import com.intellij.ide.startup.impl.StartupManagerImpl +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.Presentation +import com.intellij.openapi.actionSystem.ex.ActionManagerEx +import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.startup.StartupManager +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess +import com.intellij.psi.PsiManager +import com.intellij.psi.search.FileTypeIndex +import com.intellij.psi.search.ProjectScope +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.LoggedErrorProcessor +import org.apache.log4j.Logger +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.kotlin.idea.facet.configureFacet +import org.jetbrains.kotlin.idea.facet.getOrCreateFacet +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestMetadata +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.rethrow +import java.io.File +import java.io.IOException +import java.util.* +import kotlin.reflect.full.findAnnotation + +abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFixtureTestCaseBase() { + private val exceptions = ArrayList() + + protected val module: Module get() = myFixture.module + + protected open val captureExceptions = true + + override fun setUp() { + super.setUp() + (StartupManager.getInstance(project) as StartupManagerImpl).runPostStartupActivities() + VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory()) + + project.getComponent(EditorTracker::class.java)?.projectOpened() + + invalidateLibraryCache(project) + + if (captureExceptions) { + LoggedErrorProcessor.setNewInstance(object : LoggedErrorProcessor() { + override fun processError(message: String?, t: Throwable?, details: Array?, logger: Logger) { + exceptions.addIfNotNull(t) + super.processError(message, t, details, logger) + } + }) + } + CodeInsightTestCase.fixTemplates() + } + + override fun tearDown() { + LoggedErrorProcessor.restoreDefaultProcessor() + + VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory()) + + doKotlinTearDown(project) { + super.tearDown() + } + + if (exceptions.isNotEmpty()) { + exceptions.forEach { it.printStackTrace() } + throw AssertionError("Exceptions in other threads happened") + } + } + + override fun getProjectDescriptor(): LightProjectDescriptor + = getProjectDescriptorFromFileDirective() + + protected fun getProjectDescriptorFromTestName(): LightProjectDescriptor { + val testName = StringUtil.toLowerCase(getTestName(false)) + + if (testName.endsWith("runtime")) { + return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + } + else if (testName.endsWith("stdlib")) { + return ProjectDescriptorWithStdlibSources.INSTANCE + } + + return KotlinLightProjectDescriptor.INSTANCE + } + + protected fun getProjectDescriptorFromFileDirective(): LightProjectDescriptor { + if (!isAllFilesPresentInTest()) { + try { + val fileText = FileUtil.loadFile(File(testDataPath, fileName()), true) + + val withLibraryDirective = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "WITH_LIBRARY:") + if (!withLibraryDirective.isEmpty()) { + return SdkAndMockLibraryProjectDescriptor( + PluginTestCaseBase.getTestDataPathBase() + "/" + withLibraryDirective.get( + 0 + ), true + ) + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_SOURCES")) { + return ProjectDescriptorWithStdlibSources.INSTANCE + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_KOTLIN_TEST")) { + return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_WITH_KOTLIN_TEST + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_FULL_JDK")) { + return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_FULL_JDK + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_REFLECT")) { + return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_WITH_REFLECT + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME") || + InTextDirectivesUtils.isDirectiveDefined(fileText, "WITH_RUNTIME")) { + return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "JS")) { + return KotlinStdJSProjectDescriptor + } + else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "ENABLE_MULTIPLATFORM")) { + return KotlinProjectDescriptorWithFacet.KOTLIN_STABLE_WITH_MULTIPLATFORM + } + } + catch (e: IOException) { + throw rethrow(e) + } + } + return KotlinLightProjectDescriptor.INSTANCE + } + + protected fun isAllFilesPresentInTest(): Boolean = KotlinTestUtils.isAllFilesPresentTest(getTestName(false)) + + protected open fun fileName(): String + = KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt") + + protected fun performNotWriteEditorAction(actionId: String): Boolean { + val dataContext = (myFixture.editor as EditorEx).dataContext + + val managerEx = ActionManagerEx.getInstanceEx() + val action = managerEx.getAction(actionId) + val event = AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, Presentation(), managerEx, 0) + + action.update(event) + if (!event.presentation.isEnabled) { + return false + } + + managerEx.fireBeforeActionPerformed(action, dataContext, event) + action.actionPerformed(event) + + managerEx.fireAfterActionPerformed(action, dataContext, event) + return true + } + + override fun getTestDataPath(): String { + return this::class.findAnnotation()?.value ?: super.getTestDataPath() + } +} + +fun configureCompilerOptions(fileText: String, project: Project, module: Module) { + val version = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// LANGUAGE_VERSION: ") + val jvmTarget = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// JVM_TARGET: ") + val options = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// COMPILER_ARGUMENTS: ") + + if (version != null || jvmTarget != null || options != null) { + configureLanguageAndApiVersion(project, module, version ?: LanguageVersion.LATEST_STABLE.versionString) + + val facetSettings = KotlinFacet.get(module)!!.configuration.settings + + if (jvmTarget != null) { + (facetSettings.compilerArguments as K2JVMCompilerArguments).jvmTarget = jvmTarget + } + + if (options != null) { + val compilerSettings = facetSettings.compilerSettings ?: CompilerSettings().also { + facetSettings.compilerSettings = it + } + compilerSettings.additionalArguments = options + facetSettings.updateMergedArguments() + } + } +} + +fun configureLanguageAndApiVersion( + project: Project, + module: Module, + languageVersion: String, + apiVersion: String? = null +) { + val accessToken = WriteAction.start() + try { + val modelsProvider = IdeModifiableModelsProviderImpl(project) + val facet = module.getOrCreateFacet(modelsProvider, useProjectSettings = false) + facet.configureFacet(languageVersion, LanguageFeature.State.DISABLED, null, modelsProvider) + if (apiVersion != null) { + facet.configuration.settings.apiLevel = LanguageVersion.fromVersionString(apiVersion) + } + modelsProvider.commit() + } + finally { + accessToken.finish() + } +} + +fun Project.allKotlinFiles(): List { + val virtualFiles = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, ProjectScope.getProjectScope(this)) + return virtualFiles + .map { PsiManager.getInstance(this).findFile(it) } + .filterIsInstance() +} + +fun Project.findFileWithCaret() = + allKotlinFiles().single { "" in VfsUtilCore.loadText(it.virtualFile) } diff --git a/idea/src/META-INF/android-lint.xml b/idea/src/META-INF/android-lint.xml index dd099603e07..aba27acb6cb 100644 --- a/idea/src/META-INF/android-lint.xml +++ b/idea/src/META-INF/android-lint.xml @@ -1,118 +1,9 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/idea/src/META-INF/android-lint.xml.173 b/idea/src/META-INF/android-lint.xml.173 new file mode 100644 index 00000000000..dd099603e07 --- /dev/null +++ b/idea/src/META-INF/android-lint.xml.173 @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/src/META-INF/android.xml b/idea/src/META-INF/android.xml index 34f5f836571..4cf452c0bff 100644 --- a/idea/src/META-INF/android.xml +++ b/idea/src/META-INF/android.xml @@ -100,6 +100,10 @@ + + + + diff --git a/idea/src/META-INF/android.xml.173 b/idea/src/META-INF/android.xml.173 new file mode 100644 index 00000000000..34f5f836571 --- /dev/null +++ b/idea/src/META-INF/android.xml.173 @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + 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 b/idea/src/META-INF/plugin.xml index c96669475fd..8308eceb8b2 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -6,7 +6,7 @@ @snapshot@ JetBrains - + com.intellij.modules.platform com.intellij.modules.remoteServers @@ -355,7 +355,6 @@ - diff --git a/idea/src/META-INF/plugin.xml.173 b/idea/src/META-INF/plugin.xml.173 new file mode 100644 index 00000000000..c96669475fd --- /dev/null +++ b/idea/src/META-INF/plugin.xml.173 @@ -0,0 +1,2883 @@ + + org.jetbrains.kotlin + + Kotlin + Kotlin language support + @snapshot@ + JetBrains + + + + com.intellij.modules.platform + com.intellij.modules.remoteServers + + + org.jetbrains.kotlin.native.platform.deps + + JUnit + org.jetbrains.plugins.gradle + org.jetbrains.idea.maven + TestNG-J + com.intellij.copyright + org.jetbrains.android + Coverage + com.intellij.java-i18n + org.intellij.intelliLang + org.jetbrains.java.decompiler + + + + + com.intellij.modules.java + JavaScriptDebugger + + + + + org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Factory + + + org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory + + + + org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Factory + + + org.jetbrains.kotlin.idea.completion.LookupCancelWatcher + + + org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener + + + org.jetbrains.kotlin.idea.caches.KotlinPackageContentModificationListener + + + org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider + org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider + + + org.jetbrains.kotlin.idea.scratch.ui.ScratchFileHook + + + org.jetbrains.kotlin.idea.scratch.ScratchFileModuleInfoProvider + + + + + + org.jetbrains.kotlin.idea.PluginStartupComponent + + + + org.jetbrains.kotlin.idea.versions.KotlinUpdatePluginComponent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ImportMemberIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ImportAllMembersIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyInDestructuringAssignmentIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceContainsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceInvokeIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceCallWithUnaryOperatorIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.UnfoldAssignmentToIfIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.UnfoldPropertyToIfIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.UnfoldAssignmentToWhenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.UnfoldPropertyToWhenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.UnfoldReturnToIfIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.UnfoldReturnToWhenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.DoubleBangToIfThenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToDoubleBangIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.ElvisToIfThenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.SafeAccessToIfThenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfToWhenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.WhenToIfIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FlattenWhenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.EliminateWhenSubjectIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.MergeWhenIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveUnnecessaryParenthesesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.InsertCurlyBracesToTemplateIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MoveLambdaInsideParenthesesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.declarations.SplitPropertyDeclarationIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.declarations.ConvertMemberToExtensionIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReconstructTypeInCastOrIsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ToInfixCallIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceExplicitFunctionLiteralParamWithItIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSubstringWithDropLastIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSubstringWithSubstringAfterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSubstringWithSubstringBeforeIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSubstringWithTakeIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveBracesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddBracesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertNegatedBooleanSequenceIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertBinaryExpressionWithDemorgansLawIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SimplifyBooleanWithConstantsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddForLoopIndicesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveForLoopIndicesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.loopToCallChain.UseWithIndexIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SwapBinaryExpressionIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SwapStringEqualsIgnoreCaseIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SplitIfIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceWithOrdinaryAssignmentIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertAssertToIfWithThrowIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertIfWithThrowToAssertIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveExplicitLambdaParameterTypesIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertForEachToForLoopIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToForEachFunctionCallIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToStringTemplateIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToRawStringTemplateIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToConcatenatedStringIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertFunctionToPropertyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyToFunctionIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertReceiverToParameterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertParameterToReceiverIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertPropertyInitializerToGetterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.InvertIfConditionIntention + Kotlin + + + + org.jetbrains.kotlin.idea.refactoring.move.changePackage.ChangePackageIntention + Kotlin + + + + org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ChangeVisibilityModifierIntention$Public + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ChangeVisibilityModifierIntention$Private + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ChangeVisibilityModifierIntention$Protected + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ChangeVisibilityModifierIntention$Internal + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceUnderscoreWithParameterNameIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddJvmOverloadsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddJvmStaticIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.IterateExpressionIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxIntention + Kotlin + + + + org.jetbrains.kotlin.idea.quickfix.AddConstModifierIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.IntroduceBackingPropertyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.JoinDeclarationAndAssignmentIntention + Kotlin + + + + org.jetbrains.kotlin.idea.testIntegration.KotlinCreateTestIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddOperatorModifierIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.DestructureIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AnonymousFunctionToLambdaIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberAsConstructorParameterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction$Intention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MoveMemberOutOfCompanionObjectIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.CreateKotlinSubClassIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ToRawStringLiteralIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ToOrdinaryStringLiteralIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.IntroduceVariableIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveSingleExpressionStringTemplateIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceUntilWithRangeToIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveEmptyParenthesesFromLambdaCallIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertCamelCaseTestFunctionToSpacedIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSingleLineLetIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.copyConcatenatedStringToClipboard.CopyConcatenatedStringToClipboardIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceMathMaxWithCoerceAtLeastIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceMathMinWithCoerceAtMostIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertPrimaryConstructorToSecondaryIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertSecondaryConstructorToPrimaryIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSizeCheckWithIsNotEmptyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSizeZeroCheckWithIsEmptyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveEmptyClassBodyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertEnumToSealedClassIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertSealedClassToEnumIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveRedundantCallsOfConversionMethodsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveEmptyPrimaryConstructorIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveEmptySecondaryConstructorBodyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertTryFinallyToUseCallIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddNamesToCallArgumentsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeParameterToReceiverIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeReceiverToParameterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertTwoComparisonsToRangeCheckIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertRangeCheckToTwoComparisonsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RenameFileToMatchClassIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertObjectLiteralToClassIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MergeIfsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddMissingDestructuringIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToApplyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToWithIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToRunIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MovePropertyToClassBodyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MovePropertyToConstructorIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddOpenModifierIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ValToObjectIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ChopParameterListIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ChopArgumentListIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.NullableBooleanEqualityCheckToElvisIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceAddWithPlusAssignIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertClassToSealedClassIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddPropertyAccessorsIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddPropertyGetterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddPropertySetterIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.MoveMemberToTopLevelIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ReplaceSubstringWithIndexingOperationIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertUnsafeCastCallToUnsafeCastIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertUnsafeCastToUnsafeCastCallIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.RemoveLabeledReturnInLambdaIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.AddAnnotationUseSiteTargetIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.JoinParameterListIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.JoinArgumentListIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertLineCommentToBlockCommentIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertBlockCommentToLineCommentIntention + Kotlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt index bc809416c2e..690f8e1a0e9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt @@ -98,7 +98,7 @@ class JavaToKotlinAction : AnAction() { var converterResult: JavaToKotlinConverter.FilesResult? = null fun convert() { val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices) - converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator) + converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator!!) } @@ -117,7 +117,7 @@ class JavaToKotlinAction : AnAction() { ProgressManager.getInstance().runProcessWithProgressSynchronously( { runReadAction { - externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator) + externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator!!) } }, title, diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.173 b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.173 new file mode 100644 index 00000000000..bc809416c2e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt.173 @@ -0,0 +1,216 @@ +/* + * 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): 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 (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/editor/KotlinStringTemplateBackspaceHandler.kt b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinStringTemplateBackspaceHandler.kt index 10fbd523476..fd2bb4abada 100644 --- a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinStringTemplateBackspaceHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinStringTemplateBackspaceHandler.kt @@ -25,7 +25,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtFile class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() { - override fun beforeCharDeleted(c: Char, file: PsiFile?, editor: Editor?) { + override fun beforeCharDeleted(c: Char, file: PsiFile, editor: Editor) { if (c != '{' || file !is KtFile || !CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return val offset = editor?.caretModel?.offset ?: return @@ -38,7 +38,7 @@ class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() { editor.document.deleteString(offset, offset + 1) } - override fun charDeleted(c: Char, file: PsiFile?, editor: Editor?): Boolean { + override fun charDeleted(c: Char, file: PsiFile, editor: Editor): Boolean { return false } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinStringTemplateBackspaceHandler.kt.173 b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinStringTemplateBackspaceHandler.kt.173 new file mode 100644 index 00000000000..10fbd523476 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinStringTemplateBackspaceHandler.kt.173 @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2016 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.editor + +import com.intellij.codeInsight.CodeInsightSettings +import com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtFile + +class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() { + override fun beforeCharDeleted(c: Char, file: PsiFile?, editor: Editor?) { + if (c != '{' || file !is KtFile || !CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return + + val offset = editor?.caretModel?.offset ?: return + + val highlighter = (editor as EditorEx).highlighter + val iterator = highlighter.createIterator(offset) + if (iterator.tokenType != KtTokens.LONG_TEMPLATE_ENTRY_END) return + iterator.retreat() + if (iterator.tokenType != KtTokens.LONG_TEMPLATE_ENTRY_START) return + editor.document.deleteString(offset, offset + 1) + } + + override fun charDeleted(c: Char, file: PsiFile?, editor: Editor?): Boolean { + return false + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt index 703dac4ee96..8e39006566e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt @@ -49,20 +49,9 @@ import org.jetbrains.kotlin.util.findCallableMemberBySignature import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.ifEmpty import java.util.ArrayList -import kotlin.collections.Collection import kotlin.collections.HashMap import kotlin.collections.LinkedHashSet -import kotlin.collections.List -import kotlin.collections.Set -import kotlin.collections.emptyList -import kotlin.collections.emptySet import kotlin.collections.filter -import kotlin.collections.flatMap -import kotlin.collections.forEach -import kotlin.collections.getOrPut -import kotlin.collections.listOf -import kotlin.collections.mapNotNullTo -import kotlin.collections.plus class ChangeSuspendInHierarchyFix( element: KtNamedFunction, @@ -83,7 +72,7 @@ class ChangeSuspendInHierarchyFix( private fun findAllFunctionToProcess(project: Project): Set { val result = LinkedHashSet() - val progressIndicator = ProgressManager.getInstance().progressIndicator + val progressIndicator = ProgressManager.getInstance().progressIndicator!! val function = element ?: return emptySet() val functionDescriptor = function.unsafeResolveToDescriptor() as FunctionDescriptor diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt.173 b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt.173 new file mode 100644 index 00000000000..703dac4ee96 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeSuspendInHierarchyFix.kt.173 @@ -0,0 +1,193 @@ +/* + * 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.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiNamedElement +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress +import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest +import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor +import org.jetbrains.kotlin.util.findCallableMemberBySignature +import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.ifEmpty +import java.util.ArrayList +import kotlin.collections.Collection +import kotlin.collections.HashMap +import kotlin.collections.LinkedHashSet +import kotlin.collections.List +import kotlin.collections.Set +import kotlin.collections.emptyList +import kotlin.collections.emptySet +import kotlin.collections.filter +import kotlin.collections.flatMap +import kotlin.collections.forEach +import kotlin.collections.getOrPut +import kotlin.collections.listOf +import kotlin.collections.mapNotNullTo +import kotlin.collections.plus + +class ChangeSuspendInHierarchyFix( + element: KtNamedFunction, + private val addModifier: Boolean +) : KotlinQuickFixAction(element) { + override fun getFamilyName(): String { + return if (addModifier) { + "Add 'suspend' modifier to all functions in hierarchy" + } else { + "Remove 'suspend' modifier from all functions in hierarchy" + } + } + + override fun getText() = familyName + + override fun startInWriteAction() = false + + private fun findAllFunctionToProcess(project: Project): Set { + val result = LinkedHashSet() + + val progressIndicator = ProgressManager.getInstance().progressIndicator + + val function = element ?: return emptySet() + val functionDescriptor = function.unsafeResolveToDescriptor() as FunctionDescriptor + + val baseFunctionDescriptors = functionDescriptor.findTopMostOverriddables() + baseFunctionDescriptors.forEach { baseFunctionDescriptor -> + val baseClassDescriptor = baseFunctionDescriptor.containingDeclaration as? ClassDescriptor ?: return@forEach + val baseClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, baseClassDescriptor) ?: return@forEach + + val name = (baseClass as? PsiNamedElement)?.name ?: return@forEach + progressIndicator.text = "Looking for class $name inheritors..." + val classes = listOf(baseClass) + HierarchySearchRequest(baseClass, baseClass.useScope).searchInheritors() + classes.mapNotNullTo(result) { + val subClass = it.unwrapped as? KtClassOrObject ?: return@mapNotNullTo null + val classDescriptor = subClass.unsafeResolveToDescriptor() as ClassDescriptor + val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, classDescriptor.defaultType) + ?: return@mapNotNullTo null + val signatureInSubClass = baseFunctionDescriptor.substitute(substitutor) as FunctionDescriptor + val subFunctionDescriptor = classDescriptor.findCallableMemberBySignature(signatureInSubClass, true) + ?: return@mapNotNullTo null + subFunctionDescriptor.source.getPsi() as? KtNamedFunction + } + } + + return result + } + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val functions = project.runSynchronouslyWithProgress("Analyzing class hierarchy...", true) { + runReadAction { findAllFunctionToProcess(project) } + } ?: return + + runWriteAction { + functions.forEach { + if (addModifier) { + it.addModifier(KtTokens.SUSPEND_KEYWORD) + } + else { + it.removeModifier(KtTokens.SUSPEND_KEYWORD) + } + } + } + } + + companion object : KotlinIntentionActionsFactory() { + fun FunctionDescriptor.findTopMostOverriddables(): List { + val overridablesCache = HashMap>() + + fun FunctionDescriptor.getOverridables(): List { + return overridablesCache.getOrPut(this) { + val classDescriptor = containingDeclaration as? ClassDescriptorWithResolutionScopes ?: return emptyList() + DescriptorUtils.getSuperclassDescriptors(classDescriptor).flatMap { superClassDescriptor -> + if (superClassDescriptor !is ClassDescriptorWithResolutionScopes) return@flatMap emptyList() + val candidates = superClassDescriptor.unsubstitutedMemberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE) + val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, classDescriptor.defaultType) + ?: return@flatMap emptyList() + candidates.filter { + val signature = it.substitute(substitutor) as FunctionDescriptor + classDescriptor.findCallableMemberBySignature(signature, true) == this + } + } + } + } + + return DFS.dfs( + listOf(this), + { it?.getOverridables() ?: emptyList() }, + object : DFS.CollectingNodeHandler>(ArrayList()) { + override fun afterChildren(current: FunctionDescriptor) { + if (current.getOverridables().isEmpty()) { + result.add(current) + } + } + }) + } + + private fun Collection.getOverridables( + currentDescriptor: FunctionDescriptor + ): List { + val currentClassDescriptor = currentDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList() + return filter { + if (it !is FunctionDescriptor || it == currentDescriptor) return@filter false + if (it.isSuspend == currentDescriptor.isSuspend) return@filter false + val containingClassDescriptor = it.containingDeclaration as? ClassDescriptor ?: return@filter false + if (!currentClassDescriptor.isSubclassOf(containingClassDescriptor)) return@filter false + val substitutor = getTypeSubstitutor( + containingClassDescriptor.defaultType, + currentClassDescriptor.defaultType + ) ?: return@filter false + val signatureInCurrentClass = it.substitute(substitutor) ?: return@filter false + OverridingUtil.DEFAULT.isOverridableBy(signatureInCurrentClass, currentDescriptor, null).result == + OverridingUtil.OverrideCompatibilityInfo.Result.CONFLICT + } + } + + override fun doCreateActions(diagnostic: Diagnostic): List { + val currentFunction = diagnostic.psiElement as? KtNamedFunction ?: return emptyList() + val currentDescriptor = currentFunction.unsafeResolveToDescriptor() as FunctionDescriptor + Errors.CONFLICTING_OVERLOADS.cast(diagnostic).a.getOverridables(currentDescriptor).ifEmpty { return emptyList() } + + return listOf( + ChangeSuspendInHierarchyFix(currentFunction, true), + ChangeSuspendInHierarchyFix(currentFunction, false) + ) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt index de1b67fa2f3..2e9764915cc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt @@ -18,10 +18,14 @@ package org.jetbrains.kotlin.idea.quickfix.crossLanguage import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.QuickFixFactory -import com.intellij.lang.jvm.* +import com.intellij.lang.jvm.JvmClass +import com.intellij.lang.jvm.JvmElement +import com.intellij.lang.jvm.JvmModifier +import com.intellij.lang.jvm.JvmModifiersOwner import com.intellij.lang.jvm.actions.* import com.intellij.openapi.project.Project import com.intellij.psi.* +import com.intellij.psi.codeStyle.SuggestedNameInfo import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration @@ -56,6 +60,7 @@ import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.storage.LockBasedStorageManager @@ -160,14 +165,14 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { private inline fun JvmElement.toKtElement() = sourceElement?.unwrapped as? T - private fun fakeParametersExpressions(parameters: List, project: Project): Array? = + private fun fakeParametersExpressions(parameters: List>>, project: Project): Array? = when { parameters.isEmpty() -> emptyArray() else -> JavaPsiFacade .getElementFactory(project) .createParameterList( - parameters.map { it.name }.toTypedArray(), - parameters.map { it.type as? PsiType ?: return null }.toTypedArray() + parameters.map { it.first.names.firstOrNull() }.toTypedArray(), + parameters.map { JvmPsiConversionHelper.getInstance(project).asPsiType(it) ?: return null }.toTypedArray() ) .parameters .map(::FakeExpressionFromParameter) @@ -243,10 +248,15 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { val modifier = request.modifier val shouldPresent = request.shouldPresent - val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier) - KtTokens.OPEN_KEYWORD to !shouldPresent - else - javaPsiModifiersMapping[modifier] to shouldPresent + //TODO: make similar to `createAddMethodActions` + val (kToken, shouldPresentMapped) = when { + modifier == JvmModifier.FINAL -> KtTokens.OPEN_KEYWORD to !shouldPresent + modifier == JvmModifier.PUBLIC && shouldPresent -> + kModifierOwner.visibilityModifierType() + ?.takeIf { it != KtTokens.DEFAULT_VISIBILITY_KEYWORD } + ?.let { it to false } ?: return emptyList() + else -> javaPsiModifiersMapping[modifier] to shouldPresent + } if (kToken == null) return emptyList() val action = if (shouldPresentMapped) @@ -256,19 +266,18 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { return listOfNotNull(action) } - override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List { + override fun createAddConstructorActions(targetClass: JvmClass, request: CreateConstructorRequest): List { val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList() - if (request.typeParameters.isNotEmpty()) return emptyList() - val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) } if (!modifierBuilder.isValid) return emptyList() - val resolutionFacade = targetKtClass.getResolutionFacade() val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType - val parameterInfos = request.parameters.mapIndexed { index, param -> - val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType - val name = param.name ?: "arg${index + 1}" + val helper = JvmPsiConversionHelper.getInstance(targetKtClass.project) + val parameters = request.parameters as List>> + val parameterInfos = parameters.mapIndexed { index, param: Pair> -> + val ktType = helper.asPsiType(param)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + val name = param.first.names.firstOrNull() ?: "arg${index + 1}" ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name)) } val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor() @@ -288,7 +297,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null val project = targetKtClass.project - val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null + val fakeParametersExpressions = fakeParametersExpressions(parameters, project) ?: return@run null QuickFixFactory.getInstance() .createChangeMethodSignatureFromUsageFix( lightMethod, @@ -369,7 +378,8 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { val resolutionFacade = targetContainer.getResolutionFacade() val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade) - val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) -> + val parameters = request.parameters as List>> + val parameterInfos = parameters.map { (suggestedNames, expectedTypes) -> ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList()) } val functionInfo = FunctionInfo( @@ -388,4 +398,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() { } return listOf(action) } -} \ No newline at end of file +} + +private fun JvmPsiConversionHelper.asPsiType(param: Pair>): PsiType? = + param.second.firstOrNull()?.theType?.let { convertType(it) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.173 b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.173 new file mode 100644 index 00000000000..de1b67fa2f3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt.173 @@ -0,0 +1,391 @@ +/* + * 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.quickfix.crossLanguage + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInsight.intention.QuickFixFactory +import com.intellij.lang.jvm.* +import com.intellij.lang.jvm.actions.* +import com.intellij.openapi.project.Project +import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.appendModifier +import org.jetbrains.kotlin.idea.quickfix.AddModifierFix +import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents +import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext +import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver +import org.jetbrains.kotlin.load.java.lazy.child +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor +import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes +import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver +import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter +import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl +import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.supertypes + +class KotlinElementActionsFactory : JvmElementActionsFactory() { + companion object { + val javaPsiModifiersMapping = mapOf( + JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD, + JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD, + JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD, + JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD + ) + } + + private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() { + override fun getText(): String = psiParam.name!! + override fun getProject(): Project = psiParam.project + override fun getParent(): PsiElement = psiParam.parent + override fun getType(): PsiType? = psiParam.type + override fun isValid(): Boolean = true + override fun getContainingFile(): PsiFile = psiParam.containingFile + override fun getReferenceName(): String? = psiParam.name + override fun resolve(): PsiElement? = psiParam + } + + private class ModifierBuilder( + private val targetContainer: KtElement, + private val allowJvmStatic: Boolean = true + ) { + private val psiFactory = KtPsiFactory(targetContainer.project) + + val modifierList = psiFactory.createEmptyModifierList() + + private fun JvmModifier.transformAndAppend(): Boolean { + javaPsiModifiersMapping[this]?.let { + modifierList.appendModifier(it) + return true + } + + when (this) { + JvmModifier.STATIC -> { + if (allowJvmStatic && targetContainer is KtClassOrObject) { + addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) + } + } + JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD) + JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD) + else -> return false + } + + return true + } + + var isValid = true + private set + + fun addJvmModifier(modifier: JvmModifier) { + isValid = isValid && modifier.transformAndAppend() + } + + fun addJvmModifiers(modifiers: Iterable) { + modifiers.forEach { addJvmModifier(it) } + } + + fun addAnnotation(fqName: FqName) { + if (!isValid) return + modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}")) + } + } + + class CreatePropertyFix( + private val targetClass: JvmClass, + contextElement: KtElement, + propertyInfo: PropertyInfo + ) : CreateCallableFromUsageFix(contextElement, listOf(propertyInfo)) { + override fun getFamilyName() = "Add property" + override fun getText(): String { + val info = callableInfos.first() as PropertyInfo + return buildString { + append("Add '") + if (info.isLateinitPreferred) { + append("lateinit ") + } + append(if (info.writable) "var" else "val") + append("' property '${info.name}' to '${targetClass.name}'") + } + } + } + + private fun JvmClass.toKtClassOrFile(): KtElement? { + val psi = sourceElement + return when (psi) { + is KtClassOrObject -> psi + is KtLightClassForSourceDeclaration -> psi.kotlinOrigin + is KtLightClassForFacade -> psi.files.firstOrNull() + else -> null + } + } + + private inline fun JvmElement.toKtElement() = sourceElement?.unwrapped as? T + + private fun fakeParametersExpressions(parameters: List, project: Project): Array? = + when { + parameters.isEmpty() -> emptyArray() + else -> JavaPsiFacade + .getElementFactory(project) + .createParameterList( + parameters.map { it.name }.toTypedArray(), + parameters.map { it.type as? PsiType ?: return null }.toTypedArray() + ) + .parameters + .map(::FakeExpressionFromParameter) + .toTypedArray() + } + + private fun PsiType.collectTypeParameters(): List { + val results = ArrayList() + accept( + object : PsiTypeVisitor() { + override fun visitArrayType(arrayType: PsiArrayType) { + arrayType.componentType.accept(this) + } + + override fun visitClassType(classType: PsiClassType) { + (classType.resolve() as? PsiTypeParameter)?.let { results += it } + classType.parameters.forEach { it.accept(this) } + } + + override fun visitWildcardType(wildcardType: PsiWildcardType) { + wildcardType.bound?.accept(this) + } + } + ) + return results + } + + private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? { + val typeParameters = collectTypeParameters() + val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java) + val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null } + val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy")) + val dummyClassDescriptor = ClassDescriptorImpl( + dummyPackageDescriptor, + Name.identifier("Dummy"), + Modality.FINAL, + ClassKind.CLASS, + emptyList(), + SourceElement.NO_SOURCE, + false, + LockBasedStorageManager.NO_LOCKS + ) + val typeParameterResolver = object : TypeParameterResolver { + override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? { + val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi + val index = typeParameters.indexOf(psiTypeParameter) + if (index < 0) return null + return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor) + } + } + val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver) + val attributes = JavaTypeAttributes(TypeUsage.COMMON) + return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true) + } + + private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo { + val candidateTypes = flatMapTo(LinkedHashSet()) { + val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList() + when (it.theKind) { + ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType) + ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes() + } + } + if (candidateTypes.isEmpty()) { + val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType + return TypeInfo(nullableAnyType, Variance.INVARIANT) + } + return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList()) + } + + override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List { + val kModifierOwner = target.toKtElement() ?: return emptyList() + + val modifier = request.modifier + val shouldPresent = request.shouldPresent + val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier) + KtTokens.OPEN_KEYWORD to !shouldPresent + else + javaPsiModifiersMapping[modifier] to shouldPresent + if (kToken == null) return emptyList() + + val action = if (shouldPresentMapped) + AddModifierFix.createIfApplicable(kModifierOwner, kToken) + else + RemoveModifierFix(kModifierOwner, kToken, false) + return listOfNotNull(action) + } + + override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List { + val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList() + + if (request.typeParameters.isNotEmpty()) return emptyList() + + val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetKtClass.getResolutionFacade() + val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType + val parameterInfos = request.parameters.mapIndexed { index, param -> + val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + val name = param.name ?: "arg${index + 1}" + ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name)) + } + val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor() + val constructorInfo = ConstructorInfo( + parameterInfos, + targetKtClass, + isPrimary = needPrimary, + modifierList = modifierBuilder.modifierList, + withBody = true + ) + val addConstructorAction = object : CreateCallableFromUsageFix(targetKtClass, listOf(constructorInfo)) { + override fun getFamilyName() = "Add method" + override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'" + } + + val changePrimaryConstructorAction = run { + val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null + val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null + val project = targetKtClass.project + val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null + QuickFixFactory.getInstance() + .createChangeMethodSignatureFromUsageFix( + lightMethod, + fakeParametersExpressions, + PsiSubstitutor.EMPTY, + targetKtClass, + false, + 2 + ).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) } + } + + return listOfNotNull(changePrimaryConstructorAction, addConstructorAction) + } + + override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List { + val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + + val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetContainer.getResolutionFacade() + val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType + val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + val propertyInfo = PropertyInfo( + request.propertyName, + TypeInfo.Empty, + TypeInfo(ktType, Variance.INVARIANT), + request.setterRequired, + listOf(targetContainer), + modifierList = modifierBuilder.modifierList, + withInitializer = true + ) + val propertyInfos = if (request.setterRequired) { + listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true)) + } + else { + listOf(propertyInfo) + } + return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + } + + override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List { + val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + + val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply { + addJvmModifiers(request.modifiers) + addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) + } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetContainer.getResolutionFacade() + val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade) + val writable = JvmModifier.FINAL !in request.modifiers + val propertyInfo = PropertyInfo( + request.fieldName, + TypeInfo.Empty, + typeInfo, + writable, + listOf(targetContainer), + isForCompanion = JvmModifier.STATIC in request.modifiers, + modifierList = modifierBuilder.modifierList, + withInitializer = true + ) + val propertyInfos = if (writable) { + listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true)) + } + else { + listOf(propertyInfo) + } + return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + } + + override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List { + val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + + val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetContainer.getResolutionFacade() + val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade) + val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) -> + ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList()) + } + val functionInfo = FunctionInfo( + request.methodName, + TypeInfo.Empty, + returnTypeInfo, + listOf(targetContainer), + parameterInfos, + isForCompanion = JvmModifier.STATIC in request.modifiers, + modifierList = modifierBuilder.modifierList, + preferEmptyBody = true + ) + val action = object : CreateCallableFromUsageFix(targetContainer, listOf(functionInfo)) { + override fun getFamilyName() = "Add method" + override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'" + } + return listOf(action) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt index ec465aa70ae..1d90a098c4c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt @@ -54,9 +54,8 @@ class KotlinCallerChooser( previousTree: Tree?, callback: Consumer> ): CallerChooserBase(declaration, project, title, previousTree, "dummy." + KotlinFileType.EXTENSION, callback) { - override fun createTreeNode(method: PsiElement?, called: com.intellij.util.containers.HashSet, cancelCallback: Runnable): KotlinMethodNode { - return KotlinMethodNode(method, called, myProject, cancelCallback) - } + override fun createTreeNodeFor(method: PsiElement?, called: HashSet?, cancelCallback: Runnable?) = + KotlinMethodNode(method, called ?: HashSet(), myProject, cancelCallback ?: Runnable {}) override fun findDeepestSuperMethods(method: PsiElement) = method.toLightMethods().singleOrNull()?.findDeepestSuperMethods() diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt.173 new file mode 100644 index 00000000000..ec465aa70ae --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt.173 @@ -0,0 +1,124 @@ +/* + * 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.refactoring.changeSignature.ui + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClassOwner +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiReference +import com.intellij.psi.search.searches.MethodReferencesSearch +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.refactoring.changeSignature.CallerChooserBase +import com.intellij.refactoring.changeSignature.MethodNodeBase +import com.intellij.ui.ColoredTreeCellRenderer +import com.intellij.ui.JBColor +import com.intellij.ui.SimpleTextAttributes +import com.intellij.ui.treeStructure.Tree +import com.intellij.util.Consumer +import com.intellij.util.ui.UIUtil +import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMethodDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.hierarchy.calls.CalleeReferenceProcessor +import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallHierarchyNodeDescriptor +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import java.util.* + +class KotlinCallerChooser( + declaration: PsiElement, + project: Project, + title: String, + previousTree: Tree?, + callback: Consumer> +): CallerChooserBase(declaration, project, title, previousTree, "dummy." + KotlinFileType.EXTENSION, callback) { + override fun createTreeNode(method: PsiElement?, called: com.intellij.util.containers.HashSet, cancelCallback: Runnable): KotlinMethodNode { + return KotlinMethodNode(method, called, myProject, cancelCallback) + } + + override fun findDeepestSuperMethods(method: PsiElement) = + method.toLightMethods().singleOrNull()?.findDeepestSuperMethods() + + override fun getEmptyCallerText() = + "Caller text \nwith highlighted callee call would be shown here" + + override fun getEmptyCalleeText() = + "Callee text would be shown here" +} + +class KotlinMethodNode( + method: PsiElement?, + called: HashSet, + project: Project, + cancelCallback: Runnable +): MethodNodeBase(method?.namedUnwrappedElement ?: method, called, project, cancelCallback) { + override fun createNode(caller: PsiElement, called: HashSet) = + KotlinMethodNode(caller, called, myProject, myCancelCallback) + + override fun customizeRendererText(renderer: ColoredTreeCellRenderer) { + val descriptor = when (myMethod) { + is KtFunction -> myMethod.unsafeResolveToDescriptor() as FunctionDescriptor + is KtClass -> (myMethod.unsafeResolveToDescriptor() as ClassDescriptor).unsubstitutedPrimaryConstructor ?: return + is PsiMethod -> myMethod.getJavaMethodDescriptor() ?: return + else -> throw AssertionError("Invalid declaration: ${myMethod.getElementTextWithContext()}") + } + val containerName = generateSequence(descriptor) { it.containingDeclaration } + .firstOrNull { it is ClassDescriptor } + ?.name + + val renderedFunction = KotlinCallHierarchyNodeDescriptor.renderNamedFunction(descriptor) + val renderedFunctionWithContainer = + containerName?.let { + "${if (it.isSpecial) "[Anonymous]" else it.asString()}.$renderedFunction" + } ?: renderedFunction + + val attributes = if (isEnabled) + SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground()) + else + SimpleTextAttributes.EXCLUDED_ATTRIBUTES + renderer.append(renderedFunctionWithContainer, attributes) + + val packageName = (myMethod.containingFile as? PsiClassOwner)?.packageName ?: "" + renderer.append(" ($packageName)", SimpleTextAttributes(SimpleTextAttributes.STYLE_ITALIC, JBColor.GRAY)) + } + + override fun computeCallers(): List { + if (myMethod == null) return emptyList() + + val callers = LinkedHashSet() + + val processor = object: CalleeReferenceProcessor(false) { + override fun onAccept(ref: PsiReference, element: PsiElement) { + if ((element is KtFunction || element is KtClass || element is PsiMethod) && element !in myCalled) { + callers.add(element) + } + } + } + val query = myMethod.getRepresentativeLightMethod()?.let { MethodReferencesSearch.search(it, it.useScope, true) } + ?: ReferencesSearch.search(myMethod, myMethod.useScope) + query.forEach { processor.process(it) } + return callers.toList() + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt index 87f5eb105ef..89310e492f5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt @@ -86,7 +86,7 @@ class CopyKotlinDeclarationDialog( Pass { setErrorText(it, destinationComboBox) }, packageNameField.childComponent ) - classNameField.text = UsageViewUtil.getShortName(declaration) + classNameField.setText(UsageViewUtil.getShortName(declaration)) classNameField.selectAll() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt.173 new file mode 100644 index 00000000000..87f5eb105ef --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/copy/CopyKotlinDeclarationDialog.kt.173 @@ -0,0 +1,184 @@ +/* + * 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.refactoring.copy + +import com.intellij.ide.util.DirectoryChooser +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.JavaProjectRootsUtil +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiManager +import com.intellij.refactoring.HelpID +import com.intellij.refactoring.MoveDestination +import com.intellij.refactoring.PackageWrapper +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog +import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox +import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo +import com.intellij.ui.EditorTextField +import com.intellij.ui.RecentsManager +import com.intellij.ui.ReferenceEditorComboWithBrowseButton +import com.intellij.usageView.UsageViewUtil +import com.intellij.util.IncorrectOperationException +import com.intellij.util.ui.FormBuilder +import com.intellij.util.ui.UIUtil +import org.jetbrains.annotations.NonNls +import org.jetbrains.kotlin.idea.core.getPackage +import org.jetbrains.kotlin.idea.refactoring.Pass +import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly +import org.jetbrains.kotlin.idea.util.sourceRoot +import org.jetbrains.kotlin.name.FqNameUnsafe +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import java.awt.BorderLayout +import java.awt.Font +import javax.swing.JComponent +import javax.swing.JLabel +import javax.swing.JPanel + +// Based on com.intellij.refactoring.copy.CopyClassDialog +class CopyKotlinDeclarationDialog( + declaration: KtNamedDeclaration, + private val defaultTargetDirectory: PsiDirectory?, + private val project: Project +) : DialogWrapper(project, true) { + private val informationLabel = JLabel() + private val classNameField = EditorTextField("") + private val packageLabel = JLabel() + private lateinit var packageNameField: ReferenceEditorComboWithBrowseButton + private val openInEditorCheckBox = CopyFilesOrDirectoriesDialog.createOpenInEditorCB() + private val destinationComboBox = object : DestinationFolderComboBox() { + override fun getTargetPackage() = packageNameField.text.trim() + override fun reportBaseInTestSelectionInSource() = true + } + + private val originalFile = declaration.containingFile + + var targetDirectory: MoveDestination? = null + private set + + val targetSourceRoot: VirtualFile? + get() = ((destinationComboBox.comboBox.selectedItem as? DirectoryChooser.ItemWrapper)?.directory ?: originalFile).sourceRoot + + init { + informationLabel.text = RefactoringBundle.message("copy.class.copy.0.1", UsageViewUtil.getType(declaration), UsageViewUtil.getLongName(declaration)) + informationLabel.font = informationLabel.font.deriveFont(Font.BOLD) + + init() + + destinationComboBox.setData( + project, + defaultTargetDirectory, + Pass { setErrorText(it, destinationComboBox) }, + packageNameField.childComponent + ) + classNameField.text = UsageViewUtil.getShortName(declaration) + classNameField.selectAll() + } + + override fun getPreferredFocusedComponent() = classNameField + + override fun createCenterPanel() = JPanel(BorderLayout()) + + override fun createNorthPanel(): JComponent? { + val qualifiedName = qualifiedName + packageNameField = PackageNameReferenceEditorCombo(qualifiedName, project, RECENTS_KEY, RefactoringBundle.message("choose.destination.package")) + packageNameField.setTextFieldPreferredWidth(Math.max(qualifiedName.length + 5, 40)) + packageLabel.text = RefactoringBundle.message("destination.package") + packageLabel.labelFor = packageNameField + + val label = JLabel(RefactoringBundle.message("target.destination.folder")) + val isMultipleSourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project).size > 1 + destinationComboBox.isVisible = isMultipleSourceRoots + label.isVisible = isMultipleSourceRoots + label.labelFor = destinationComboBox + + val panel = JPanel(BorderLayout()) + panel.add(openInEditorCheckBox, BorderLayout.EAST) + return FormBuilder.createFormBuilder() + .addComponent(informationLabel) + .addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), classNameField, UIUtil.LARGE_VGAP) + .addLabeledComponent(packageLabel, packageNameField) + .addLabeledComponent(label, destinationComboBox) + .addComponent(panel) + .panel + } + + private val qualifiedName: String + get() = defaultTargetDirectory?.getPackage()?.qualifiedName ?: "" + + val newName: String? + get() = classNameField.text + + val openInEditor: Boolean + get() = openInEditorCheckBox.isSelected + + private fun checkForErrors(): String? { + val packageName = packageNameField.text + val newName = newName + + val manager = PsiManager.getInstance(project) + + if (packageName.isNotEmpty() && !FqNameUnsafe(packageName).hasIdentifiersOnly()) { + return RefactoringBundle.message("invalid.target.package.name.specified") + } + + if (newName.isNullOrEmpty()) { + return RefactoringBundle.message("no.class.name.specified") + } + + try { + targetDirectory = destinationComboBox.selectDirectory(PackageWrapper(manager, packageName), false) + } + catch (e: IncorrectOperationException) { + return e.message + } + + targetDirectory?.getTargetIfExists(defaultTargetDirectory)?.let { + val targetFileName = newName + "." + originalFile.virtualFile.extension + if (it.findFile(targetFileName) == originalFile) { + return "Can't copy class to the containing file" + } + } + + return null + } + + override fun doOKAction() { + val packageName = packageNameField.text + + val errorString = checkForErrors() + if (errorString != null) { + if (errorString.isNotEmpty()) { + Messages.showMessageDialog(project, errorString, RefactoringBundle.message("error.title"), Messages.getErrorIcon()) + } + classNameField.requestFocusInWindow() + return + } + + RecentsManager.getInstance(project).registerRecentEntry(RECENTS_KEY, packageName) + CopyFilesOrDirectoriesDialog.saveOpenInEditorState(openInEditorCheckBox.isSelected) + + super.doOKAction() + } + + override fun getHelpId() = HelpID.COPY_CLASS + + companion object { + @NonNls private val RECENTS_KEY = "CopyKotlinDeclarationDialog.RECENTS_KEY" + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt index b343b713442..e1d388c5a6a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt @@ -73,8 +73,8 @@ class KotlinAwareDelegatingMoveDestination( } filesToProcess.flatMap {it.declarations}.forEach { it.accept(extraElementCollector) } - val progressIndicator = ProgressManager.getInstance().progressIndicator - progressIndicator?.pushState() + val progressIndicator = ProgressManager.getInstance().progressIndicator!! + progressIndicator.pushState() val extraUsages = ArrayList() try { @@ -87,7 +87,7 @@ class KotlinAwareDelegatingMoveDestination( } } finally { - progressIndicator?.popState() + progressIndicator.popState() } filesToProcess.forEach { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt.173 new file mode 100644 index 00000000000..b343b713442 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareDelegatingMoveDestination.kt.173 @@ -0,0 +1,97 @@ +/* + * 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.refactoring.move.moveClassesOrPackages + +import com.intellij.openapi.progress.ProgressManager +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiPackage +import com.intellij.psi.PsiRecursiveElementWalkingVisitor +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.refactoring.MoveDestination +import com.intellij.usageView.UsageInfo +import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.idea.refactoring.move.createMoveUsageInfoIfPossible +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile +import org.jetbrains.kotlin.idea.search.projectScope +import org.jetbrains.kotlin.idea.stubindex.KotlinExactPackagesIndex +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf + +class KotlinAwareDelegatingMoveDestination( + private val delegate: MoveDestination, + private val targetPackage: PsiPackage?, + private val targetDirectory: PsiDirectory? +) : MoveDestination by delegate { + override fun analyzeModuleConflicts( + elements: MutableCollection, + conflicts: MultiMap, + usages: Array + ) { + delegate.analyzeModuleConflicts(elements, conflicts, usages) + + if (targetPackage == null || targetDirectory == null) return + + val project = targetDirectory.project + val moveTarget = KotlinDirectoryMoveTarget(FqName(targetPackage.qualifiedName), targetDirectory) + val packagesIndex = KotlinExactPackagesIndex.getInstance() + val directoriesToMove = elements.flatMapTo(LinkedHashSet()) { + (it as? PsiPackage)?.directories?.toList() ?: emptyList() + } + val projectScope = project.projectScope() + val filesToProcess = elements.flatMapTo(LinkedHashSet()) { + if (it is PsiPackage) packagesIndex[it.qualifiedName, project, projectScope] else emptyList() + } + + val extraElementsForReferenceSearch = LinkedHashSet() + val extraElementCollector = object : PsiRecursiveElementWalkingVisitor() { + override fun visitElement(element: PsiElement) { + if (element is KtNamedDeclaration && element.hasModifier(KtTokens.INTERNAL_KEYWORD)) { + element.parentsWithSelf.lastOrNull { it is KtNamedDeclaration }?.let { extraElementsForReferenceSearch += it } + stopWalking() + } + super.visitElement(element) + } + } + filesToProcess.flatMap {it.declarations}.forEach { it.accept(extraElementCollector) } + + val progressIndicator = ProgressManager.getInstance().progressIndicator + progressIndicator?.pushState() + + val extraUsages = ArrayList() + try { + progressIndicator.text = "Looking for Usages" + for ((index, element) in extraElementsForReferenceSearch.withIndex()) { + progressIndicator.fraction = (index + 1)/extraElementsForReferenceSearch.size.toDouble() + ReferencesSearch.search(element, projectScope).mapNotNullTo(extraUsages) { ref -> + createMoveUsageInfoIfPossible(ref, element, true, false) + } + } + } + finally { + progressIndicator?.popState() + } + + filesToProcess.forEach { + analyzeConflictsInFile(it, extraUsages, moveTarget, directoriesToMove, conflicts) {} + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt index 4a822f93164..16965b54536 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt @@ -28,8 +28,8 @@ class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog( elementsToMove: Array, moveCallback: MoveCallback? ) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) { - override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination { - val delegate = super.createDestination(aPackage, directory) + override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination? { + val delegate = super.createDestination(aPackage, directory) ?: return null return KotlinAwareDelegatingMoveDestination(delegate, aPackage, directory) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt.173 new file mode 100644 index 00000000000..4a822f93164 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveClassesOrPackages/KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog.kt.173 @@ -0,0 +1,35 @@ +/* + * 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.refactoring.move.moveClassesOrPackages + +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiPackage +import com.intellij.refactoring.MoveDestination +import com.intellij.refactoring.move.MoveCallback +import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesToNewDirectoryDialog + +class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog( + directory: PsiDirectory, + elementsToMove: Array, + moveCallback: MoveCallback? +) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) { + override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination { + val delegate = super.createDestination(aPackage, directory) + return KotlinAwareDelegatingMoveDestination(delegate, aPackage, directory) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt index 75b792df31c..17b3dd8efd1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt @@ -163,7 +163,7 @@ sealed class MoveDeclarationsDelegate { if (lightOuterClass != null) { MoveInnerClassUsagesHandler.EP_NAME .forLanguage(usage.element!!.language) - ?.correctInnerClassUsage(usage, lightOuterClass) + ?.correctInnerClassUsage(usage, lightOuterClass, outerInstanceParameterName) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt.173 new file mode 100644 index 00000000000..75b792df31c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt.173 @@ -0,0 +1,182 @@ +/* + * Copyright 2010-2016 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.refactoring.move.moveDeclarations + +import com.intellij.psi.PsiElement +import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler +import com.intellij.refactoring.util.MoveRenameUsageInfo +import com.intellij.usageView.UsageInfo +import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.codeInsight.shorten.isToBeShortened +import org.jetbrains.kotlin.idea.refactoring.move.* +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf + +sealed class MoveDeclarationsDelegate { + abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo + + open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List = emptyList() + + open fun collectConflicts( + descriptor: MoveDeclarationsDescriptor, + internalUsages: MutableSet, + conflicts: MultiMap + ) { + + } + + open fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) { + + } + + open fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List) { + + } + + object TopLevel : MoveDeclarationsDelegate() { + override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo { + val sourcePackage = ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName) + val targetPackage = moveTarget.targetContainerFqName?.let { ContainerInfo.Package(it) } ?: ContainerInfo.UnknownPackage + return ContainerChangeInfo(sourcePackage, targetPackage) + } + } + + class NestedClass( + val newClassName: String? = null, + val outerInstanceParameterName: String? = null + ) : MoveDeclarationsDelegate() { + override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo { + val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!) + val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject + val targetContainerFqName = moveTarget.targetContainerFqName + val newInfo = when { + targetContainerFqName == null -> ContainerInfo.UnknownPackage + movingToClass -> ContainerInfo.Class(targetContainerFqName) + else -> ContainerInfo.Package(targetContainerFqName) + } + return ContainerChangeInfo(originalInfo, newInfo) + } + + override fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List { + val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass ?: return emptyList() + return collectOuterInstanceReferences(classToMove) + } + + private fun isValidTargetForImplicitCompanionAsDispatchReceiver( + moveDescriptor: MoveDeclarationsDescriptor, + companionDescriptor: ClassDescriptor + ): Boolean { + val moveTarget = moveDescriptor.moveTarget + return when (moveTarget) { + is KotlinMoveTargetForCompanion -> true + is KotlinMoveTargetForExistingElement -> { + val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false + val targetClassDescriptor = targetClass.unsafeResolveToDescriptor() as ClassDescriptor + val companionClassDescriptor = companionDescriptor.containingDeclaration as? ClassDescriptor ?: return false + targetClassDescriptor.isSubclassOf(companionClassDescriptor) + } + else -> false + } + } + + override fun collectConflicts( + descriptor: MoveDeclarationsDescriptor, + internalUsages: MutableSet, + conflicts: MultiMap + ) { + val usageIterator = internalUsages.iterator() + while (usageIterator.hasNext()) { + val usage = usageIterator.next() + val element = usage.element ?: continue + + val isConflict = when (usage) { + is ImplicitCompanionAsDispatchReceiverUsageInfo -> { + if (!isValidTargetForImplicitCompanionAsDispatchReceiver(descriptor, usage.companionDescriptor)) { + conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}") + } + true + } + + is OuterInstanceReferenceUsageInfo -> usage.reportConflictIfAny(conflicts) + + else -> false + } + if (isConflict) { + usageIterator.remove() + } + } + } + + override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) { + with(originalDeclaration) { + newClassName?.let { setName(it) } + + if (this is KtClass) { + if ((descriptor.moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement !is KtClassOrObject) { + if (hasModifier(KtTokens.INNER_KEYWORD)) removeModifier(KtTokens.INNER_KEYWORD) + if (hasModifier(KtTokens.PROTECTED_KEYWORD)) removeModifier(KtTokens.PROTECTED_KEYWORD) + } + + if (outerInstanceParameterName != null) { + val type = (containingClassOrObject!!.unsafeResolveToDescriptor() as ClassDescriptor).defaultType + val parameter = KtPsiFactory(project) + .createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}") + createPrimaryConstructorParameterListIfAbsent().addParameter(parameter).isToBeShortened = true + } + } + } + } + + override fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List) { + if (outerInstanceParameterName == null) return + val psiFactory = KtPsiFactory(descriptor.project) + val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName) + val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass + + for (usage in usages) { + if (usage is MoveRenameUsageInfo) { + val referencedNestedClass = usage.referencedElement?.unwrapped as? KtClassOrObject + if (referencedNestedClass == classToMove) { + val outerClass = referencedNestedClass?.containingClassOrObject + val lightOuterClass = outerClass?.toLightClass() + if (lightOuterClass != null) { + MoveInnerClassUsagesHandler.EP_NAME + .forLanguage(usage.element!!.language) + ?.correctInnerClassUsage(usage, lightOuterClass) + } + } + } + + when (usage) { + is OuterInstanceReferenceUsageInfo.ExplicitThis -> { + usage.expression?.replace(newOuterInstanceRef) + } + is OuterInstanceReferenceUsageInfo.ImplicitReceiver -> { + usage.callElement?.let { it.replace(psiFactory.createExpressionByPattern("$0.$1", outerInstanceParameterName, it)) } + } + } + } + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt index de3600c69ae..7cb16ec5d5c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories +import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiElement @@ -85,11 +86,11 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() { for ((index, usageInfo) in infos.withIndex()) { if (usageInfo !is FileUsagesWrapper) continue - project.runSynchronouslyWithProgress("Analyzing conflicts in ${usageInfo.psiFile.name}", false) { - runReadAction { - analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) { - infos[index] = usageInfo.copy(usages = it) - } + ProgressManager.getInstance().progressIndicator?.text2 = "Processing ${usageInfo.psiFile.name}" + + runReadAction { + analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) { + infos[index] = usageInfo.copy(usages = it) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt.173 new file mode 100644 index 00000000000..de3600c69ae --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt.173 @@ -0,0 +1,150 @@ +/* + * 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.refactoring.move.moveFilesOrDirectories + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper +import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil +import com.intellij.usageView.UsageInfo +import com.intellij.util.Function +import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.idea.core.getPackage +import org.jetbrains.kotlin.idea.core.quoteIfNeeded +import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile +import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile +import java.util.* + +class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() { + private data class FileUsagesWrapper( + val psiFile: KtFile, + val usages: List, + val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor? + ) : UsageInfo(psiFile) + + private class MoveContext( + val newParent: PsiDirectory, + val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor? + ) + + private val fileHandler = MoveKotlinFileHandler() + + private var fileToMoveContext: MutableMap? = null + + private fun getOrCreateMoveContextMap(): MutableMap { + return fileToMoveContext ?: HashMap().apply { + fileToMoveContext = this + invokeOnceOnCommandFinish { fileToMoveContext = null } + } + } + + override fun findUsages( + filesToMove: MutableCollection, + directoriesToMove: Array, + result: MutableCollection, + searchInComments: Boolean, + searchInNonJavaFiles: Boolean, + project: Project) { + filesToMove + .filterIsInstance() + .mapTo(result) { FileUsagesWrapper(it, fileHandler.findUsages(it, null, false), null) } + } + + override fun preprocessUsages( + project: Project, + files: MutableSet, + infos: Array, + directory: PsiDirectory?, + conflicts: MultiMap + ) { + val psiPackage = directory?.getPackage() ?: return + val moveTarget = KotlinDirectoryMoveTarget(FqName(psiPackage.qualifiedName), directory) + for ((index, usageInfo) in infos.withIndex()) { + if (usageInfo !is FileUsagesWrapper) continue + + project.runSynchronouslyWithProgress("Analyzing conflicts in ${usageInfo.psiFile.name}", false) { + runReadAction { + analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) { + infos[index] = usageInfo.copy(usages = it) + } + } + } + } + } + + override fun beforeMove(psiFile: PsiFile) { + + } + + // Actual move logic is implemented in postProcessUsages since usages are not available here + override fun move( + file: PsiFile, + moveDestination: PsiDirectory, + oldToNewElementsMapping: MutableMap, + movedFiles: MutableList, + listener: RefactoringElementListener? + ): Boolean { + if (file !is KtFile) return false + + val moveDeclarationsProcessor = fileHandler.initMoveProcessor(file, moveDestination, false) + val moveContextMap = getOrCreateMoveContextMap() + moveContextMap[file] = MoveContext(moveDestination, moveDeclarationsProcessor) + if (moveDeclarationsProcessor != null) { + moveDestination.getPackage()?.let { newPackage -> + file.packageDirective?.fqName = FqName(newPackage.qualifiedName).quoteIfNeeded() + } + } + return true + } + + override fun afterMove(newElement: PsiElement) { + + } + + override fun postProcessUsages(usages: Array, newDirMapper: Function) { + val fileToMoveContext = fileToMoveContext ?: return + try { + val usagesToProcess = ArrayList() + usages + .filterIsInstance() + .forEach body@ { + val file = it.psiFile + val moveContext = fileToMoveContext[file] ?: return@body + + MoveFilesOrDirectoriesUtil.doMoveFile(file, moveContext.newParent) + + val moveDeclarationsProcessor = moveContext.moveDeclarationsProcessor ?: return@body + val movedFile = moveContext.newParent.findFile(file.name) ?: return@body + + usagesToProcess += FileUsagesWrapper(movedFile as KtFile, it.usages, moveDeclarationsProcessor) + } + usagesToProcess.forEach { fileHandler.retargetUsages(it.usages, it.moveDeclarationsProcessor!!) } + } + finally { + this.fileToMoveContext = null + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt index ba039fbf423..5eb65ea3d24 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt @@ -91,11 +91,10 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() { override fun findCollisions( element: PsiElement, - newName: String?, + newName: String, allRenames: MutableMap, result: MutableList ) { - if (newName == null) return val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor @@ -122,7 +121,7 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() { else -> null } - override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { val simpleUsages = ArrayList(usages.size) val ambiguousImportUsages = com.intellij.util.SmartList() for (usage in usages) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt.173 new file mode 100644 index 00000000000..ba039fbf423 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt.173 @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2016 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.refactoring.rename + +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenamePsiElementProcessor +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.utils.SmartList +import java.util.* + +class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement): Boolean { + return element is KtClassOrObject || element is KtLightClass || element is KtConstructor<*> || element is KtTypeAlias + } + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS = enabled + } + + override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS + + override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS = enabled + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor?) = getClassOrObject(element) + + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap) { + super.prepareRenaming(element, newName, allRenames) + + val classOrObject = getClassOrObject(element) as? KtClassOrObject ?: return + + val file = classOrObject.containingKtFile + + val virtualFile = file.virtualFile + if (virtualFile != null) { + val nameWithoutExtensions = virtualFile.nameWithoutExtension + if (nameWithoutExtensions == classOrObject.name) { + val newFileName = newName + "." + virtualFile.extension + allRenames.put(file, newFileName) + RenamePsiElementProcessor.forElement(file).prepareRenaming(file, newFileName, allRenames) + } + } + } + + override fun findReferences(element: PsiElement): Collection { + if (element is KtObjectDeclaration && element.isCompanion()) { + return super.findReferences(element).filter { !it.isCompanionObjectClassReference() } + } + return super.findReferences(element) + } + + private fun PsiReference.isCompanionObjectClassReference(): Boolean { + if (this !is KtSimpleNameReference) { + return false + } + val bindingContext = element.analyze(BodyResolveMode.PARTIAL) + return bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] != null + } + + override fun findCollisions( + element: PsiElement, + newName: String?, + allRenames: MutableMap, + result: MutableList + ) { + if (newName == null) return + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return + val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor + + val collisions = SmartList() + checkRedeclarations(descriptor, newName, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + result += collisions + } + + private fun getClassOrObject(element: PsiElement?): PsiElement? = when (element) { + is KtLightClass -> + when (element) { + is KtLightClassForSourceDeclaration -> element.kotlinOrigin + is KtLightClassForFacade -> element + else -> throw AssertionError("Should not be suggested to rename element of type " + element::class.java + " " + element) + } + + is KtConstructor<*> -> + element.getContainingClassOrObject() + + is KtClassOrObject, is KtTypeAlias -> element + + else -> null + } + + override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + val simpleUsages = ArrayList(usages.size) + val ambiguousImportUsages = com.intellij.util.SmartList() + for (usage in usages) { + if (usage.isAmbiguousImportUsage()) { + ambiguousImportUsages += usage + } + else { + simpleUsages += usage + } + } + element.ambiguousImportUsages = ambiguousImportUsages + + super.renameElement(element, newName, simpleUsages.toTypedArray(), listener) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt index b63a1d89b2e..d45e55e829f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt @@ -40,7 +40,7 @@ class RenameKotlinFileProcessor : RenamePsiFileProcessor() { override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element) - override fun prepareRenaming(element: PsiElement?, + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt.173 new file mode 100644 index 00000000000..b63a1d89b2e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt.173 @@ -0,0 +1,65 @@ +/* + * 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.refactoring.rename + +import com.intellij.openapi.fileTypes.FileTypeManager +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElement +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.SearchScope +import com.intellij.refactoring.rename.RenamePsiFileProcessor +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils +import org.jetbrains.kotlin.psi.KtFile + +class RenameKotlinFileProcessor : RenamePsiFileProcessor() { + class FileRenamingPsiClassWrapper( + private val psiClass: KtLightClass, + private val file: KtFile + ) : KtLightClass by psiClass { + override fun isValid() = file.isValid + } + + override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element) + + override fun prepareRenaming(element: PsiElement?, + newName: String, + allRenames: MutableMap, + scope: SearchScope) { + val jetFile = element as? KtFile ?: return + if (FileTypeManager.getInstance().getFileTypeByFileName(newName) != KotlinFileType.INSTANCE) { + return + } + + val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return + + val fileInfo = JvmFileClassUtil.getFileClassInfoNoResolve(jetFile) + if (!fileInfo.withJvmName) { + val facadeFqName = fileInfo.facadeClassFqName + val project = jetFile.project + val facadeClass = JavaPsiFacade.getInstance(project) + .findClass(facadeFqName.asString(), GlobalSearchScope.moduleScope(module)) as? KtLightClass + if (facadeClass != null) { + allRenames[FileRenamingPsiClassWrapper(facadeClass, jetFile)] = PackagePartClassUtils.getFilePartShortName(newName) + } + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt index d86e522d867..e5622404417 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt @@ -88,11 +88,10 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { override fun findCollisions( element: PsiElement, - newName: String?, + newName: String, allRenames: Map, result: MutableList ) { - if (newName == null) return val declaration = element.unwrapped as? KtNamedFunction ?: return val descriptor = declaration.unsafeResolveToDescriptor() checkConflictsAndReplaceUsageInfos(element, allRenames, result) @@ -115,7 +114,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected() - override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? { + override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? { substituteForExpectOrActual(element)?.let { return it } val wrappedMethod = wrapPsiMethod(element) ?: return element @@ -179,11 +178,9 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { } } - override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap, scope: SearchScope) { + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { super.prepareRenaming(element, newName, allRenames, scope) - if (newName == null) return - if (element is KtLightMethod && getJvmName(element) == null) { (element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName } } @@ -213,7 +210,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { } } - override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { val simpleUsages = ArrayList(usages.size) val ambiguousImportUsages = SmartList() for (usage in usages) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt.173 new file mode 100644 index 00000000000..d86e522d867 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt.173 @@ -0,0 +1,248 @@ +/* + * Copyright 2010-2016 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.refactoring.rename + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Pass +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenameDialog +import com.intellij.refactoring.rename.RenameJavaMethodProcessor +import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.refactoring.rename.RenameUtil +import com.intellij.refactoring.util.RefactoringUtil +import com.intellij.usageView.UsageInfo +import com.intellij.util.SmartList +import org.jetbrains.kotlin.asJava.LightClassUtil +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.refactoring.Pass +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup +import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsKotlinAware +import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsNoWrapping +import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.util.liftToExpected +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.DescriptorUtils +import java.lang.IllegalStateException +import java.util.* + +class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { + private val javaMethodProcessorInstance = RenameJavaMethodProcessor() + + override fun canProcessElement(element: PsiElement): Boolean { + return element is KtNamedFunction || (element is KtLightMethod && element.kotlinOrigin is KtNamedFunction) || element is FunctionWithSupersWrapper + } + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled + } + + override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD + + override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD = enabled + } + + private fun getJvmName(element: PsiElement): String? { + val descriptor = (element.unwrapped as? KtFunction)?.unsafeResolveToDescriptor() as? FunctionDescriptor ?: return null + return DescriptorUtils.getJvmName(descriptor) + } + + override fun findReferences(element: PsiElement): Collection { + val allReferences = super.findReferences(element) + return when { + getJvmName(element) == null -> allReferences + element is KtElement -> allReferences.filter { it is KtReference } + element is KtLightElement<*, *> -> allReferences.filterNot { it is KtReference } + else -> emptyList() + } + } + + override fun findCollisions( + element: PsiElement, + newName: String?, + allRenames: Map, + result: MutableList + ) { + if (newName == null) return + val declaration = element.unwrapped as? KtNamedFunction ?: return + val descriptor = declaration.unsafeResolveToDescriptor() + checkConflictsAndReplaceUsageInfos(element, allRenames, result) + result += SmartList().also { collisions -> + checkRedeclarations(descriptor, newName, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + } + } + + class FunctionWithSupersWrapper( + val originalDeclaration: KtNamedFunction, + val supers: List + ) : KtLightElement, PsiNamedElement by originalDeclaration { + override val kotlinOrigin: KtNamedFunction? + get() = originalDeclaration + override val clsDelegate: KtNamedFunction + get() = originalDeclaration + } + + private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected() + + override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? { + substituteForExpectOrActual(element)?.let { return it } + + val wrappedMethod = wrapPsiMethod(element) ?: return element + + val deepestSuperMethods = findDeepestSuperMethodsKotlinAware(wrappedMethod) + val substitutedJavaElement = when { + deepestSuperMethods.isEmpty() -> return element + wrappedMethod.isConstructor || deepestSuperMethods.size == 1 || element !is KtNamedFunction -> { + javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor) + } + else -> { + val chosenElements = checkSuperMethods(element, null, "rename") + if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod + } + } + + if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) { + return substitutedJavaElement.kotlinOrigin as? KtNamedFunction + } + + return substitutedJavaElement + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass) { + fun preprocessAndPass(substitutedJavaElement: PsiElement) { + val elementToProcess = if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) { + substitutedJavaElement.kotlinOrigin as? KtNamedFunction + } + else { + substitutedJavaElement + } + renameCallback.pass(elementToProcess) + } + + substituteForExpectOrActual(element)?.let { return preprocessAndPass(it) } + + val wrappedMethod = wrapPsiMethod(element) + val deepestSuperMethods = if (wrappedMethod != null) { + findDeepestSuperMethodsKotlinAware(wrappedMethod) + } else { + findDeepestSuperMethodsNoWrapping(element) + } + when { + deepestSuperMethods.isEmpty() -> preprocessAndPass(element) + wrappedMethod != null && (wrappedMethod.isConstructor || element !is KtNamedFunction) -> { + javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor, Pass(::preprocessAndPass)) + } + else -> { + val declaration = element.unwrapped as? KtNamedFunction ?: return + checkSuperMethodsWithPopup(declaration, deepestSuperMethods.toList(), "Rename", editor) { + preprocessAndPass(if (it.size > 1) FunctionWithSupersWrapper(declaration, it) else wrappedMethod ?: element) + } + } + } + } + + override fun createRenameDialog(project: Project, element: PsiElement, nameSuggestionContext: PsiElement?, editor: Editor?): RenameDialog { + val elementForDialog = (element as? FunctionWithSupersWrapper)?.originalDeclaration ?: element + return object : RenameDialog(project, elementForDialog, nameSuggestionContext, editor) { + override fun createRenameProcessor(newName: String) = RenameProcessor(getProject(), element, newName, isSearchInComments, isSearchInNonJavaFiles) + } + } + + override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap, scope: SearchScope) { + super.prepareRenaming(element, newName, allRenames, scope) + + if (newName == null) return + + if (element is KtLightMethod && getJvmName(element) == null) { + (element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName } + } + if (element is FunctionWithSupersWrapper) { + allRenames.remove(element) + } + for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) { + val psiMethod = wrapPsiMethod(declaration) ?: continue + allRenames[declaration] = newName + if (psiMethod.containingClass != null) { + psiMethod.forEachOverridingMethod { it -> + val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it + + if (overrider is SyntheticElement) return@forEachOverridingMethod true + + val overriderName = overrider.name + val baseName = psiMethod.name + val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newName) + if (newOverriderName != null) { + RenameProcessor.assertNonCompileElement(overrider) + allRenames.put(overrider, newOverriderName) + } + return@forEachOverridingMethod true + } + javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope) + } + } + } + + override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + val simpleUsages = ArrayList(usages.size) + val ambiguousImportUsages = SmartList() + for (usage in usages) { + if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) { + usage.apply() + continue + } + + if (usage.isAmbiguousImportUsage()) { + ambiguousImportUsages += usage + } + else { + simpleUsages += usage + } + } + element.ambiguousImportUsages = ambiguousImportUsages + + RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + + (element.unwrapped as? KtNamedDeclaration)?.let(::dropOverrideKeywordIfNecessary) + } + + private fun wrapPsiMethod(element: PsiElement?): PsiMethod? = when (element) { + is PsiMethod -> element + is KtNamedFunction, is KtSecondaryConstructor -> runReadAction { + LightClassUtil.getLightClassMethod(element as KtFunction) + } + else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt index 377548707bd..69f24550b5f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt @@ -39,11 +39,10 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() { override fun findCollisions( element: PsiElement, - newName: String?, + newName: String, allRenames: MutableMap, result: MutableList ) { - if (newName == null) return val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor @@ -54,7 +53,7 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() { result += collisions } - override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { super.renameElement(element, newName, usages, listener) usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt.173 new file mode 100644 index 00000000000..377548707bd --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt.173 @@ -0,0 +1,62 @@ +/* + * 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.refactoring.rename + +import com.intellij.psi.PsiElement +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.utils.SmartList + +class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement) = element is KtParameter && element.ownerFunction is KtFunction + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE = enabled + } + + override fun findCollisions( + element: PsiElement, + newName: String?, + allRenames: MutableMap, + result: MutableList + ) { + if (newName == null) return + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return + val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor + + val collisions = SmartList() + checkRedeclarations(descriptor, newName, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + result += collisions + } + + override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + super.renameElement(element, newName, usages, listener) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt index 6e1bc177eac..b30984cbeee 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt @@ -196,11 +196,10 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { override fun findCollisions( element: PsiElement, - newName: String?, + newName: String, allRenames: MutableMap, result: MutableList ) { - if (newName == null) return val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor @@ -237,8 +236,8 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { } } - override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? { - val namedUnwrappedElement = element?.namedUnwrappedElement ?: return null + override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? { + val namedUnwrappedElement = element.namedUnwrappedElement ?: return null val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration ?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()") @@ -293,7 +292,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { override fun copy() = this } - override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap, scope: SearchScope) { + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { super.prepareRenaming(element, newName, allRenames, scope) val namedUnwrappedElement = element.namedUnwrappedElement @@ -303,7 +302,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()") } - val newPropertyName = if (newName != null && element is KtLightMethod) propertyNameByAccessor(newName, element) else newName + val newPropertyName = if (element is KtLightMethod) propertyNameByAccessor(newName, element) else newName val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt.173 new file mode 100644 index 00000000000..6e1bc177eac --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt.173 @@ -0,0 +1,479 @@ +/* + * Copyright 2010-2016 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.refactoring.rename + +import com.intellij.navigation.NavigationItem +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.Pass +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.DirectClassInheritorsSearch +import com.intellij.psi.search.searches.OverridingMethodsSearch +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.refactoring.util.MoveRenameUsageInfo +import com.intellij.refactoring.util.RefactoringUtil +import com.intellij.usageView.UsageInfo +import com.intellij.usageView.UsageViewUtil +import org.jetbrains.kotlin.asJava.* +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations +import org.jetbrains.kotlin.idea.core.isEnumCompanionPropertyWithEntryConflict +import org.jetbrains.kotlin.idea.core.unquote +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup +import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary +import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName +import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.util.findCallableMemberBySignature +import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.SmartList + +class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement): Boolean { + val namedUnwrappedElement = element.namedUnwrappedElement + return namedUnwrappedElement is KtProperty || (namedUnwrappedElement is KtParameter && namedUnwrappedElement.hasValOrVar()) + } + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled + } + + override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD + + override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD = enabled + } + + private fun getJvmNames(element: PsiElement): Pair { + val descriptor = (element.unwrapped as? KtDeclaration)?.unsafeResolveToDescriptor() as? PropertyDescriptor ?: return null to null + val getterName = descriptor.getter?.let { DescriptorUtils.getJvmName(it) } + val setterName = descriptor.setter?.let { DescriptorUtils.getJvmName(it) } + return getterName to setterName + } + + override fun findReferences(element: PsiElement): Collection { + val allReferences = super.findReferences(element).filterNot { it is KtDestructuringDeclarationReference } + val (getterJvmName, setterJvmName) = getJvmNames(element) + return when { + getterJvmName == null && setterJvmName == null -> allReferences + element is KtElement -> allReferences.filter { + it is KtReference + || (getterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != setterJvmName) + || (setterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != getterJvmName) + } + element is KtLightDeclaration<*, *> -> { + val name = element.name + if (name == getterJvmName || name == setterJvmName) allReferences.filterNot { it is KtReference } else allReferences + } + else -> emptyList() + } + } + + private fun checkAccidentalOverrides( + declaration: KtNamedDeclaration, + newName: String, + descriptor: VariableDescriptor, + result: MutableList + ) { + fun reportAccidentalOverride(candidate: PsiNamedElement) { + val what = UsageViewUtil.getType(declaration).capitalize() + val withWhat = candidate.renderDescription() + val where = candidate.representativeContainer()?.renderDescription() ?: return + val message = "$what after rename will clash with existing $withWhat in $where" + result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message) + } + + if (descriptor !is PropertyDescriptor) return + val initialClass = declaration.containingClassOrObject ?: return + val initialClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return + + val prototype = object : PropertyDescriptor by descriptor { + override fun getName() = Name.guessByFirstCharacter(newName) + } + + DFS.dfs( + listOf(initialClassDescriptor), + DFS.Neighbors { DescriptorUtils.getSuperclassDescriptors(it) }, + object : DFS.AbstractNodeHandler() { + override fun beforeChildren(current: ClassDescriptor): Boolean { + if (current == initialClassDescriptor) return true + (current.findCallableMemberBySignature(prototype))?.let { candidateDescriptor -> + val candidate = candidateDescriptor.source.getPsi() as? PsiNamedElement ?: return false + reportAccidentalOverride(candidate) + return false + } + return true + } + + override fun result() { + + } + } + ) + + if (!declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + val initialPsiClass = initialClass.toLightClass() ?: return + val prototypes = declaration.toLightMethods().mapNotNull { + it as KtLightMethod + val methodName = accessorNameByPropertyName(newName, it) ?: return@mapNotNull null + object : KtLightMethod by it { + override fun getName() = methodName + } + } + DFS.dfs( + listOf(initialPsiClass), + DFS.Neighbors { DirectClassInheritorsSearch.search(it) }, + object : DFS.AbstractNodeHandler() { + override fun beforeChildren(current: PsiClass): Boolean { + if (current == initialPsiClass) return true + + if (current is KtLightClass) { + val property = current.kotlinOrigin?.findPropertyByName(newName) ?: return true + reportAccidentalOverride(property) + return false + } + + for (psiMethod in prototypes) { + current.findMethodBySignature(psiMethod, false)?.let { + val candidate = it.unwrapped as? PsiNamedElement ?: return true + reportAccidentalOverride(candidate) + return false + } + } + + return true + } + + override fun result() { + + } + } + ) + } + } + + override fun findCollisions( + element: PsiElement, + newName: String?, + allRenames: MutableMap, + result: MutableList + ) { + if (newName == null) return + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return + val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor + + val collisions = SmartList() + checkRedeclarations(descriptor, newName, collisions) + checkAccidentalOverrides(declaration, newName, descriptor, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + result += collisions + } + + private fun chooseCallableToRename(callableDeclaration: KtCallableDeclaration): KtCallableDeclaration? { + val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration) + if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) { + return callableDeclaration + } + + if (ApplicationManager.getApplication()!!.isUnitTestMode) return deepestSuperDeclaration + + val containsText: String? = + deepestSuperDeclaration.fqName?.parent()?.asString() ?: + (deepestSuperDeclaration.parent as? KtClassOrObject)?.name + + val result = Messages.showYesNoCancelDialog( + deepestSuperDeclaration.project, + if (containsText != null) "Do you want to rename base property from \n$containsText" else "Do you want to rename base property", + "Rename warning", + Messages.getQuestionIcon()) + + return when (result) { + Messages.YES -> deepestSuperDeclaration + Messages.NO -> callableDeclaration + else -> /* Cancel rename */ null + } + } + + override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? { + val namedUnwrappedElement = element?.namedUnwrappedElement ?: return null + + val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration + ?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + + val declarationToRename = chooseCallableToRename(callableDeclaration) ?: return null + + val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) + if (element is KtLightMethod) { + val name = element.name + if (element.name != getterJvmName && element.name != setterJvmName) return declarationToRename + return declarationToRename.toLightMethods().firstOrNull { it.name == name } + } + + return declarationToRename + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass) { + val namedUnwrappedElement = element.namedUnwrappedElement ?: return + + val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration + ?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + + fun preprocessAndPass(substitutedJavaElement: PsiElement) { + val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) + val elementToProcess = if (element is KtLightMethod) { + val name = element.name + if (element.name != getterJvmName && element.name != setterJvmName) { + substitutedJavaElement + } + else { + substitutedJavaElement.toLightMethods().firstOrNull { it.name == name } + } + } + else substitutedJavaElement + renameCallback.pass(elementToProcess) + } + + val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration) + if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) { + return preprocessAndPass(callableDeclaration) + } + + val superPsiMethods = listOfNotNull(deepestSuperDeclaration.getRepresentativeLightMethod()) + checkSuperMethodsWithPopup(callableDeclaration, superPsiMethods, "Rename", editor) { + preprocessAndPass(if (it.size > 1) deepestSuperDeclaration else callableDeclaration) + } + } + + class PropertyMethodWrapper(private val propertyMethod: PsiMethod) : PsiNamedElement by propertyMethod, NavigationItem by propertyMethod { + override fun getName() = propertyMethod.name + override fun setName(name: String) = this + override fun copy() = this + } + + override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap, scope: SearchScope) { + super.prepareRenaming(element, newName, allRenames, scope) + + val namedUnwrappedElement = element.namedUnwrappedElement + val propertyMethods = when(namedUnwrappedElement) { + is KtProperty -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) } + is KtParameter -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) } + else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + } + + val newPropertyName = if (newName != null && element is KtLightMethod) propertyNameByAccessor(newName, element) else newName + + val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) + + val getter = propertyMethods.getter as? KtLightMethod + val setter = propertyMethods.setter as? KtLightMethod + if (newPropertyName != null + && getter != null && setter != null + && (element == getter || element == setter) + && propertyNameByAccessor(getter.name, getter) == propertyNameByAccessor(setter.name, setter)) { + val accessorToRename = if (element == getter) setter else getter + val newAccessorName = if (element == getter) JvmAbi.setterName(newPropertyName) else JvmAbi.getterName(newPropertyName) + if (ApplicationManager.getApplication().isUnitTestMode + || Messages.showYesNoDialog("Do you want to rename ${accessorToRename.name}() as well?", + "Rename", + Messages.getQuestionIcon()) == Messages.YES) { + allRenames[accessorToRename] = newAccessorName + } + } + + for (propertyMethod in propertyMethods) { + if (element is KtDeclaration && newPropertyName != null) { + val wrapper = PropertyMethodWrapper(propertyMethod) + when { + JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null -> + allRenames[wrapper] = JvmAbi.getterName(newPropertyName) + JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null -> + allRenames[wrapper] = JvmAbi.setterName(newPropertyName) + } + } + addRenameElements(propertyMethod, (element as PsiNamedElement).name, newPropertyName, allRenames, scope) + } + } + + private enum class UsageKind { + SIMPLE_PROPERTY_USAGE, + GETTER_USAGE, + SETTER_USAGE + } + + override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { + val newNameUnquoted = newName.unquote() + if (element is KtLightMethod) { + if (element.modifierList.findAnnotation(DescriptorUtils.JVM_NAME.asString()) != null) { + return super.renameElement(element, newName, usages, listener) + } + + val origin = element.kotlinOrigin + val newPropertyName = propertyNameByAccessor(newNameUnquoted, element) + // Kotlin references to Kotlin property should not use accessor name + if (newPropertyName != null && (origin is KtProperty || origin is KtParameter)) { + val (ktUsages, otherUsages) = usages.partition { it.reference is KtSimpleNameReference } + super.renameElement(element, newName, otherUsages.toTypedArray(), listener) + renameElement(origin, newPropertyName.quoteIfNeeded(), ktUsages.toTypedArray(), listener) + return + } + } + + if (element !is KtProperty && element !is KtParameter) { + super.renameElement(element, newName, usages, listener) + return + } + + val name = (element as KtNamedDeclaration).name!! + val oldGetterName = JvmAbi.getterName(name) + val oldSetterName = JvmAbi.setterName(name) + + if (isEnumCompanionPropertyWithEntryConflict(element, newNameUnquoted)) { + for ((i, usage) in usages.withIndex()) { + if (usage !is MoveRenameUsageInfo) continue + val ref = usage.reference ?: continue + // TODO: Enum value can't be accessed from Java in case of conflict with companion member + if (ref is KtReference) { + val newRef = (ref.bindToElement(element) as? KtSimpleNameExpression)?.mainReference ?: continue + usages[i] = MoveRenameUsageInfo(newRef, usage.referencedElement) + } + } + } + + val adjustedUsages = if (element is KtParameter) usages.filterNot { + val refTarget = it.reference?.resolve() + refTarget is KtLightMethod && DataClassDescriptorResolver.isComponentLike(Name.guessByFirstCharacter(refTarget.name)) + } else usages.toList() + + val refKindUsages = adjustedUsages.groupBy { usage: UsageInfo -> + val refElement = usage.reference?.resolve() + if (refElement is PsiMethod) { + when (refElement.name) { + oldGetterName -> UsageKind.GETTER_USAGE + oldSetterName -> UsageKind.SETTER_USAGE + else -> UsageKind.SIMPLE_PROPERTY_USAGE + } + } + else { + UsageKind.SIMPLE_PROPERTY_USAGE + } + } + + super.renameElement(element, JvmAbi.setterName(newNameUnquoted).quoteIfNeeded(), + refKindUsages[UsageKind.SETTER_USAGE]?.toTypedArray() ?: arrayOf(), + null) + + super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(), + refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf(), + null) + + super.renameElement(element, newName, + refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf(), + null) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + + dropOverrideKeywordIfNecessary(element) + + listener?.elementRenamed(element) + } + + private fun addRenameElements(psiMethod: PsiMethod?, + oldName: String?, + newName: String?, + allRenames: MutableMap, + scope: SearchScope) { + if (psiMethod == null) return + + OverridingMethodsSearch.search(psiMethod, scope, true).forEach { overrider -> + val overriderElement = overrider.namedUnwrappedElement + + if (overriderElement != null && overriderElement !is SyntheticElement) { + RenameProcessor.assertNonCompileElement(overriderElement) + + val overriderName = overriderElement.name + + if (overriderElement is PsiMethod) { + if (newName != null && Name.isValidIdentifier(newName)) { + val isGetter = overriderElement.parameterList.parametersCount == 0 + allRenames[overriderElement] = if (isGetter) JvmAbi.getterName(newName) else JvmAbi.setterName(newName) + } + } + else { + val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, newName) + if (newOverriderName != null) { + allRenames[overriderElement] = newOverriderName + } + } + } + } + } + + private fun findDeepestOverriddenDeclaration(declaration: KtCallableDeclaration): KtCallableDeclaration? { + if (declaration.modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) { + val bindingContext = declaration.analyze() + var descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] + if (descriptor is ValueParameterDescriptor) { + descriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor] + ?: return declaration + } + + if (descriptor != null) { + assert(descriptor is PropertyDescriptor) { "Property descriptor is expected" } + + val supers = (descriptor as PropertyDescriptor).getDeepestSuperDeclarations() + + // Take one of supers for now - API doesn't support substitute to several elements (IDEA-48796) + val deepest = supers.first() + if (deepest != descriptor) { + val superPsiElement = DescriptorToSourceUtils.descriptorToDeclaration(deepest) + return superPsiElement as? KtCallableDeclaration + } + } + } + + return null + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt index b0875034e34..1d5ee60760e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt @@ -59,7 +59,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() { return references } - override fun getQualifiedNameAfterRename(element: PsiElement, newName: String?, nonJava: Boolean): String? { + override fun getQualifiedNameAfterRename(element: PsiElement, newName: String, nonJava: Boolean): String? { if (!nonJava) return newName val qualifiedName = when (element) { @@ -70,9 +70,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() { return PsiUtilCore.getQualifiedNameAfterRename(qualifiedName, newName) } - override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap, scope: SearchScope) { - if (newName == null) return - + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { val safeNewName = newName.quoteIfNeeded() if (!newName.isIdentifier()) { @@ -97,9 +95,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() { && ref.multiResolve(false).mapNotNullTo(HashSet()) { it.element?.unwrapped }.size > 1 } - override fun getPostRenameCallback(element: PsiElement, newName: String?, elementListener: RefactoringElementListener?): Runnable? { - if (newName == null) return null - + override fun getPostRenameCallback(element: PsiElement, newName: String, elementListener: RefactoringElementListener): Runnable? { return Runnable { element.ambiguousImportUsages?.forEach { val ref = it.reference as? PsiPolyVariantReference ?: return@forEach diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt.173 new file mode 100644 index 00000000000..b0875034e34 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt.173 @@ -0,0 +1,124 @@ +/* + * 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.refactoring.rename + +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.MethodReferencesSearch +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.psi.util.PsiUtilCore +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenamePsiElementProcessor +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters +import org.jetbrains.kotlin.idea.search.projectScope +import org.jetbrains.kotlin.idea.util.actualsForExpected +import org.jetbrains.kotlin.idea.util.liftToExpected +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isIdentifier +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded +import org.jetbrains.kotlin.resolve.ImportPath + +abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() { + override fun canProcessElement(element: PsiElement): Boolean = element is KtNamedDeclaration + + override fun findReferences(element: PsiElement): Collection { + val searchParameters = KotlinReferencesSearchParameters( + element, + element.project.projectScope(), + kotlinOptions = KotlinReferencesSearchOptions(searchForComponentConventions = false) + ) + val references = ReferencesSearch.search(searchParameters).toMutableList() + if (element is KtNamedFunction + || (element is KtProperty && !element.isLocal) + || (element is KtParameter && element.hasValOrVar())) { + element.toLightMethods().flatMapTo(references) { MethodReferencesSearch.search(it) } + } + return references + } + + override fun getQualifiedNameAfterRename(element: PsiElement, newName: String?, nonJava: Boolean): String? { + if (!nonJava) return newName + + val qualifiedName = when (element) { + is KtNamedDeclaration -> element.fqName?.asString() ?: element.name + is PsiClass -> element.qualifiedName ?: element.name + else -> return null + } + return PsiUtilCore.getQualifiedNameAfterRename(qualifiedName, newName) + } + + override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap, scope: SearchScope) { + if (newName == null) return + + val safeNewName = newName.quoteIfNeeded() + + if (!newName.isIdentifier()) { + allRenames[element] = safeNewName + } + + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration + if (declaration != null) { + declaration.liftToExpected()?.let { expectDeclaration -> + allRenames[expectDeclaration] = safeNewName + expectDeclaration.actualsForExpected().forEach { allRenames[it] = safeNewName } + } + } + } + + protected var PsiElement.ambiguousImportUsages: List? by UserDataProperty(Key.create("AMBIGUOUS_IMPORT_USAGES")) + + protected fun UsageInfo.isAmbiguousImportUsage(): Boolean { + val ref = reference as? PsiPolyVariantReference ?: return false + val refElement = ref.element + return refElement.parents.any { (it is KtImportDirective && !it.isAllUnder) || (it is PsiImportStaticStatement && !it.isOnDemand) } + && ref.multiResolve(false).mapNotNullTo(HashSet()) { it.element?.unwrapped }.size > 1 + } + + override fun getPostRenameCallback(element: PsiElement, newName: String?, elementListener: RefactoringElementListener?): Runnable? { + if (newName == null) return null + + return Runnable { + element.ambiguousImportUsages?.forEach { + val ref = it.reference as? PsiPolyVariantReference ?: return@forEach + if (ref.multiResolve(false).isEmpty()) { + ref.handleElementRename(newName) + } + else { + ref.element?.getStrictParentOfType()?.let { importDirective -> + val fqName = importDirective.importedFqName!! + val newFqName = fqName.parent().child(Name.identifier(newName)) + val importList = importDirective.parent as KtImportList + if (importList.imports.none { it.importedFqName == newFqName }) { + val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false)) + importDirective.parent.addAfter(newImportDirective, importDirective) + } + } + } + } + element.ambiguousImportUsages = null + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt index 65a37b4b31e..86ef29c83a1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt @@ -26,11 +26,10 @@ class RenameKotlinTypeParameterProcessor : RenameKotlinPsiProcessor() { override fun findCollisions( element: PsiElement, - newName: String?, + newName: String, allRenames: MutableMap, result: MutableList ) { - if (newName == null) return val declaration = element as? KtTypeParameter ?: return val descriptor = declaration.unsafeResolveToDescriptor() checkRedeclarations(descriptor, newName, result) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt.173 new file mode 100644 index 00000000000..65a37b4b31e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt.173 @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2016 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.refactoring.rename + +import com.intellij.psi.PsiElement +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.psi.KtTypeParameter + +class RenameKotlinTypeParameterProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement) = element is KtTypeParameter + + override fun findCollisions( + element: PsiElement, + newName: String?, + allRenames: MutableMap, + result: MutableList + ) { + if (newName == null) return + val declaration = element as? KtTypeParameter ?: return + val descriptor = declaration.unsafeResolveToDescriptor() + checkRedeclarations(descriptor, newName, result) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt index 0c7102bfc3a..298af998a27 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.idea.refactoring.isTrueJavaMethod import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters +import org.jetbrains.kotlin.idea.search.usagesSearch.constructor import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages import org.jetbrains.kotlin.idea.util.actualsForExpected import org.jetbrains.kotlin.idea.util.liftToExpected @@ -238,8 +239,9 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() { fun findDelegationCallUsages(element: PsiElement) { val constructors = when (element) { - is PsiClass -> element.constructors - is PsiMethod -> arrayOf(element) + is KtClass -> element.allConstructors + is PsiClass -> element.constructors.toList() + is PsiMethod -> listOf(element) else -> return } for (constructor in constructors) { @@ -254,10 +256,8 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() { return when (element) { is KtClassOrObject -> { - element.toLightClass()?.let { klass -> - findDelegationCallUsages(klass) - findUsagesByJavaProcessor(klass, false) - } ?: findKotlinDeclarationUsages(element) + findDelegationCallUsages(element) + findKotlinDeclarationUsages(element) } is KtSecondaryConstructor -> { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt.173 b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt.173 new file mode 100644 index 00000000000..0c7102bfc3a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt.173 @@ -0,0 +1,464 @@ +/* + * 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.refactoring.safeDelete + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.Condition +import com.intellij.openapi.util.Conditions +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor +import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverrideAnnotation +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverridingMethodUsageInfo +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo +import com.intellij.usageView.UsageInfo +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.asJava.* +import org.jetbrains.kotlin.asJava.elements.KtLightField +import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.idea.core.deleteElementAndCleanParent +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods +import org.jetbrains.kotlin.idea.refactoring.formatClass +import org.jetbrains.kotlin.idea.refactoring.formatFunction +import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals +import org.jetbrains.kotlin.idea.refactoring.isTrueJavaMethod +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters +import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages +import org.jetbrains.kotlin.idea.util.actualsForExpected +import org.jetbrains.kotlin.idea.util.liftToExpected +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch +import org.jetbrains.kotlin.psi.psiUtil.parameterIndex +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.utils.SmartSet +import org.jetbrains.kotlin.utils.ifEmpty +import java.util.* + +class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() { + companion object { + @set:TestOnly + internal var Project.ALLOW_LIFTING_ACTUAL_PARAMETER_TO_EXPECTED + by NotNullableUserDataProperty(Key.create("ALLOW_LIFTING_ACTUAL_PARAMETER_TO_EXPECTED"), true) + + private var KtDeclaration.dropActualModifier: Boolean? by UserDataProperty(Key.create("DROP_ACTUAL_MODIFIER")) + } + + override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement() + + override fun findUsages( + element: PsiElement, allElementsToDelete: Array, usages: MutableList + ): NonCodeUsageSearchInfo { + val deleteSet = SmartSet.create() + deleteSet.addAll(allElementsToDelete) + + fun getIgnoranceCondition() = Condition { + if (it is KtFile) return@Condition false + deleteSet.any { element -> JavaSafeDeleteProcessor.isInside(it, element.unwrapped) } + } + + fun getSearchInfo(element: PsiElement) = NonCodeUsageSearchInfo(getIgnoranceCondition(), element) + + fun searchKotlinDeclarationReferences(declaration: KtDeclaration): Sequence { + val elementsToSearch = if (declaration is KtParameter) declaration.withExpectedActuals() else listOf(declaration) + return elementsToSearch.asSequence().flatMap { + val searchParameters = KotlinReferencesSearchParameters( + it, + it.useScope, + kotlinOptions = KotlinReferencesSearchOptions(acceptCallableOverrides = true) + ) + ReferencesSearch.search(searchParameters) + .asSequence() + .filterNot { reference -> getIgnoranceCondition().value(reference.element) } + } + } + + fun findKotlinParameterUsages(parameter: KtParameter) { + val ownerFunction = parameter.ownerFunction as? KtFunction ?: return + val index = parameter.parameterIndex() + for (reference in searchKotlinDeclarationReferences(ownerFunction)) { + val callee = reference.element as? KtExpression ?: continue + val resolvedCall = callee.resolveToCall(BodyResolveMode.FULL) ?: continue + val parameterDescriptor = resolvedCall.candidateDescriptor.valueParameters.getOrNull(index) ?: continue + val resolvedArgument = resolvedCall.valueArguments[parameterDescriptor] ?: continue + val arguments = resolvedArgument.arguments.filterIsInstance() + if (arguments.isEmpty()) continue + + usages.add(SafeDeleteValueArgumentListUsageInfo(parameter, *arguments.toTypedArray())) + } + } + + fun findKotlinDeclarationUsages(declaration: KtDeclaration): NonCodeUsageSearchInfo { + searchKotlinDeclarationReferences(declaration).mapNotNullTo(usages) { reference -> + val refElement = reference.element ?: return@mapNotNullTo null + refElement.getNonStrictParentOfType()?.let { importDirective -> + SafeDeleteImportDirectiveUsageInfo(importDirective, element) + } ?: SafeDeleteReferenceSimpleDeleteUsageInfo(refElement, declaration, false) + } + + if (declaration is KtParameter) { + findKotlinParameterUsages(declaration) + } + + return getSearchInfo(declaration) + } + + fun asLightElements(ktElements: Array) = + ktElements.flatMap { (it as? KtElement)?.toLightElements() ?: listOf(it) }.toTypedArray() + + fun findUsagesByJavaProcessor(element: PsiElement, forceReferencedElementUnwrapping: Boolean): NonCodeUsageSearchInfo? { + val javaUsages = ArrayList() + + val elementToPassToJava = when (element) { + is KtLightFieldImpl<*> -> object : KtLightField by element { + // Suppress walking through initializer compiled PSI (it doesn't contain any reference expressions anyway) + override fun getInitializer() = null + } + else -> element + } + val searchInfo = super.findUsages(elementToPassToJava, asLightElements(allElementsToDelete), javaUsages) + + javaUsages.filterIsInstance().mapNotNullTo(deleteSet) { it.element } + + val ignoranceCondition = getIgnoranceCondition() + + javaUsages.mapNotNullTo(usages) { usageInfo -> + when (usageInfo) { + is SafeDeleteOverridingMethodUsageInfo -> + usageInfo.smartPointer.element?.let { usageElement -> + KotlinSafeDeleteOverridingUsageInfo(usageElement, usageInfo.referencedElement) + } + + is SafeDeleteOverrideAnnotation -> + usageInfo.smartPointer.element?.let { usageElement -> + when { + usageElement.isTrueJavaMethod() -> usageInfo + usageElement.toLightMethods().all { method -> method.findSuperMethods().isEmpty() } -> { + KotlinSafeDeleteOverrideAnnotation(usageElement, usageInfo.referencedElement) as UsageInfo + } + else -> null + } + } + + is SafeDeleteReferenceJavaDeleteUsageInfo -> + usageInfo.element?.let { usageElement -> + when { + usageElement.getNonStrictParentOfType() != null -> null + ignoranceCondition.value(usageElement) -> null + else -> { + usageElement.getNonStrictParentOfType()?.let { importDirective -> + SafeDeleteImportDirectiveUsageInfo(importDirective, element) + } ?: usageElement.getParentOfTypeAndBranch { typeReference }?.let { + if (element is PsiClass && element.isInterface) SafeDeleteSuperTypeUsageInfo(it, element) else usageInfo + } ?: if (forceReferencedElementUnwrapping) { + SafeDeleteReferenceJavaDeleteUsageInfo(usageElement, element.unwrapped, usageInfo.isSafeDelete) + } else usageInfo + } + } + } + + else -> usageInfo + } + } + + return searchInfo + } + + fun findUsagesByJavaProcessor(elements: Sequence, insideDeleted: Condition): Condition = + elements + .mapNotNull { element -> findUsagesByJavaProcessor(element, true)?.insideDeletedCondition } + .fold(insideDeleted) { condition1, condition2 -> Conditions.or(condition1, condition2) } + + fun findUsagesByJavaProcessor(ktDeclaration: KtDeclaration): NonCodeUsageSearchInfo { + val lightElements = ktDeclaration.toLightElements() + if (lightElements.isEmpty()) { + return findKotlinDeclarationUsages(ktDeclaration) + } + return NonCodeUsageSearchInfo( + findUsagesByJavaProcessor( + lightElements.asSequence(), + getIgnoranceCondition() + ), + ktDeclaration + ) + } + + fun findTypeParameterUsages(parameter: KtTypeParameter) { + val owner = parameter.getNonStrictParentOfType() ?: return + + val parameterList = owner.typeParameters + val parameterIndex = parameterList.indexOf(parameter) + + for (reference in ReferencesSearch.search(owner)) { + if (reference !is KtReference) continue + + val referencedElement = reference.element + + val argList = referencedElement.getNonStrictParentOfType()?.typeArgumentList + ?: referencedElement.getNonStrictParentOfType()?.typeArgumentList + + if (argList != null) { + val projections = argList.arguments + if (parameterIndex < projections.size) { + usages.add(SafeDeleteTypeArgumentListUsageInfo(projections[parameterIndex], parameter)) + } + } + } + } + + fun findDelegationCallUsages(element: PsiElement) { + val constructors = when (element) { + is PsiClass -> element.constructors + is PsiMethod -> arrayOf(element) + else -> return + } + for (constructor in constructors) { + constructor.processDelegationCallConstructorUsages(constructor.useScope) { + if (!getIgnoranceCondition().value(it)) { + usages.add(SafeDeleteReferenceSimpleDeleteUsageInfo(it, element, false)) + } + true + } + } + } + + return when (element) { + is KtClassOrObject -> { + element.toLightClass()?.let { klass -> + findDelegationCallUsages(klass) + findUsagesByJavaProcessor(klass, false) + } ?: findKotlinDeclarationUsages(element) + } + + is KtSecondaryConstructor -> { + element.getRepresentativeLightMethod()?.let { method -> + findDelegationCallUsages(method) + findUsagesByJavaProcessor(method, false) + } ?: findKotlinDeclarationUsages(element) + } + + is KtNamedFunction -> { + if (element.isLocal) { + findKotlinDeclarationUsages(element) + } + else { + val lightMethods = element.toLightMethods() + if (lightMethods.isNotEmpty()) { + lightMethods.map { method -> findUsagesByJavaProcessor(method, false) }.firstOrNull() + } + else { + findKotlinDeclarationUsages(element) + } + } + } + + is PsiMethod -> { + findUsagesByJavaProcessor(element, false) + } + + is PsiClass -> { + findUsagesByJavaProcessor(element, false) + } + + is KtProperty -> { + if (element.isLocal) { + findKotlinDeclarationUsages(element) + } + else { + findUsagesByJavaProcessor(element) + } + } + + is KtTypeParameter -> { + findTypeParameterUsages(element) + findUsagesByJavaProcessor(element) + } + + is KtParameter -> + findUsagesByJavaProcessor(element) + + is KtTypeAlias -> { + findKotlinDeclarationUsages(element) + } + + else -> null + } ?: getSearchInfo(element) + } + + override fun findConflicts(element: PsiElement, allElementsToDelete: Array): MutableCollection? { + if (element is KtNamedFunction || element is KtProperty) { + val jetClass = element.getNonStrictParentOfType() + if (jetClass == null || jetClass.getBody() != element.parent) return null + + val modifierList = jetClass.modifierList + if (modifierList != null && modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return null + + val bindingContext = (element as KtElement).analyze() + + val declarationDescriptor = + bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, element] as? CallableMemberDescriptor ?: return null + + return declarationDescriptor.overriddenDescriptors + .asSequence() + .filter { overridenDescriptor -> overridenDescriptor.modality == Modality.ABSTRACT } + .mapTo(ArrayList()) { overridenDescriptor -> + KotlinBundle.message( + "x.implements.y", + formatFunction(declarationDescriptor, true), + formatClass(declarationDescriptor.containingDeclaration, true), + formatFunction(overridenDescriptor, true), + formatClass(overridenDescriptor.containingDeclaration, true) + ) + } + } + + return super.findConflicts(element, allElementsToDelete) + } + + /* + * Mostly copied from JavaSafeDeleteProcessor.preprocessUsages + * Revision: d4fc033 + * (replaced original dialog) + */ + override fun preprocessUsages(project: Project, usages: Array): Array? { + val result = ArrayList() + val overridingMethodUsages = ArrayList() + + for (usage in usages) { + if (usage is KotlinSafeDeleteOverridingUsageInfo) { + overridingMethodUsages.add(usage) + } else { + result.add(usage) + } + } + + if (!overridingMethodUsages.isEmpty()) { + if (ApplicationManager.getApplication()!!.isUnitTestMode) { + result.addAll(overridingMethodUsages) + } else { + val dialog = KotlinOverridingDialog(project, overridingMethodUsages) + dialog.show() + + if (!dialog.isOK) return null + + result.addAll(dialog.selected) + } + } + + return result.toTypedArray() + } + + override fun prepareForDeletion(element: PsiElement) { + if (element is KtDeclaration) { + element.actualsForExpected().forEach { + if (it is KtParameter) { + (it.parent as? KtParameterList)?.removeParameter(it) + } + else { + it.removeModifier(KtTokens.IMPL_KEYWORD) + it.removeModifier(KtTokens.ACTUAL_KEYWORD) + } + } + } + + when (element) { + is PsiMethod -> element.cleanUpOverrides() + + is KtNamedFunction -> + if (!element.isLocal) { + element.getRepresentativeLightMethod()?.cleanUpOverrides() + } + + is KtProperty -> + if (!element.isLocal) { + element.toLightMethods().forEach(PsiMethod::cleanUpOverrides) + } + + is KtTypeParameter -> + element.deleteElementAndCleanParent() + + is KtParameter -> { + element.ownerFunction?.let { + if (it.dropActualModifier == true) { + it.removeModifier(KtTokens.IMPL_KEYWORD) + it.removeModifier(KtTokens.ACTUAL_KEYWORD) + it.dropActualModifier = null + } + } + (element.parent as KtParameterList).removeParameter(element) + } + } + } + + private fun shouldAllowPropagationToExpected(parameter: KtParameter): Boolean { + if (ApplicationManager.getApplication().isUnitTestMode) return parameter.project.ALLOW_LIFTING_ACTUAL_PARAMETER_TO_EXPECTED + + return Messages.showYesNoDialog( + "Do you want to delete this parameter in expected declaration and all related actual ones?", + RefactoringBundle.message("safe.delete.title"), + Messages.getQuestionIcon() + ) == Messages.YES + } + + override fun getElementsToSearch( + element: PsiElement, module: Module?, allElementsToDelete: Collection + ): Collection? { + when (element) { + is KtParameter -> { + val expectParameter = element.liftToExpected() as? KtParameter + if (expectParameter != null && expectParameter != element) { + if (shouldAllowPropagationToExpected(element)) { + return listOf(expectParameter) + } else { + element.ownerFunction?.dropActualModifier = true + return listOf(element) + } + } + + return element.toPsiParameters().flatMap { psiParameter -> + checkParametersInMethodHierarchy(psiParameter) ?: emptyList() + }.ifEmpty { listOf(element) } + } + + is PsiParameter -> + return checkParametersInMethodHierarchy(element) + } + + if (ApplicationManager.getApplication()!!.isUnitTestMode) return Collections.singletonList(element) + + return when (element) { + is KtNamedFunction, is KtProperty -> checkSuperMethods(element as KtDeclaration, allElementsToDelete, "delete (with usage search)") + else -> super.getElementsToSearch(element, module, allElementsToDelete) + } + } +} diff --git a/idea/testData/android/lint/alarm.kt b/idea/testData/android/lint/alarm.kt index eaa7dfb1f70..be50e16aedc 100644 --- a/idea/testData/android/lint/alarm.kt +++ b/idea/testData/android/lint/alarm.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintShortAlarmInspection import android.app.AlarmManager diff --git a/idea/testData/android/lint/alarm.kt.173 b/idea/testData/android/lint/alarm.kt.173 new file mode 100644 index 00000000000..eaa7dfb1f70 --- /dev/null +++ b/idea/testData/android/lint/alarm.kt.173 @@ -0,0 +1,23 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection + +import android.app.AlarmManager + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class TestAlarm { + fun test(alarmManager: AlarmManager) { + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, 60000, null); // OK + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 6000, 70000, null); // OK + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 50, 10, null); // ERROR + + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, // ERROR + OtherClass.MY_INTERVAL, null); // ERROR + + val interval = 10; + val interval2 = 2L * interval; + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, interval2, null); // ERROR + } + + private object OtherClass { + val MY_INTERVAL = 1000L; + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/apiCheck.kt b/idea/testData/android/lint/apiCheck.kt index fb8e110aaea..af027213faf 100644 --- a/idea/testData/android/lint/apiCheck.kt +++ b/idea/testData/android/lint/apiCheck.kt @@ -1,7 +1,7 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection -// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection -// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection +// INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintInlinedApiInspection +// INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintOverrideInspection import android.animation.RectEvaluator import android.annotation.SuppressLint @@ -42,7 +42,7 @@ class ApiCallTest: Activity() { // Ok Bundle().getInt("") - View.SYSTEM_UI_FLAG_FULLSCREEN + View.SYSTEM_UI_FLAG_FULLSCREEN // Virtual call getActionBar() // API 11 @@ -61,18 +61,18 @@ class ApiCallTest: Activity() { GridLayout::class // Field access - val field = OpcodeInfo.MAXIMUM_VALUE // API 11 + val field = OpcodeInfo.MAXIMUM_VALUE // API 11 val fillParent = LayoutParams.FILL_PARENT // API 1 // This is a final int, which means it gets inlined val matchParent = LayoutParams.MATCH_PARENT // API 8 // Field access: non final - val batteryInfo = report!!.batteryInfo + val batteryInfo = report!!.batteryInfo // Enum access if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) { - val mode = PorterDuff.Mode.OVERLAY // API 11 + val mode = PorterDuff.Mode.OVERLAY // API 11 } } @@ -95,7 +95,7 @@ class ApiCallTest: Activity() { fun test3(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - RectEvaluator(); // ERROR + RectEvaluator(); // ERROR } } @@ -104,15 +104,15 @@ class ApiCallTest: Activity() { System.out.println("Something"); RectEvaluator(rect); // OK } else { - RectEvaluator(rect); // ERROR + RectEvaluator(rect); // ERROR } } fun test5(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) { - RectEvaluator(rect); // ERROR + RectEvaluator(rect); // ERROR } else { - RectEvaluator(rect); // ERROR + RectEvaluator(rect); // ERROR } } @@ -120,29 +120,29 @@ class ApiCallTest: Activity() { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } if (SDK_INT >= ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } else { GridLayout(null).getOrientation(); // Not flagged } @@ -150,24 +150,24 @@ class ApiCallTest: Activity() { if (Build.VERSION.SDK_INT >= 14) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } // Nested conditionals if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (priority) { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } } else { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } // Nested conditionals 2 @@ -178,7 +178,7 @@ class ApiCallTest: Activity() { GridLayout(null).getOrientation(); // Not flagged } } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } } @@ -186,41 +186,41 @@ class ApiCallTest: Activity() { if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (android.os.Build.VERSION.SDK_INT >= 16) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (android.os.Build.VERSION.SDK_INT >= 13) { - GridLayout(null).getOrientation(); // Flagged + GridLayout(null).getOrientation(); // Flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (SDK_INT >= JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } else { GridLayout(null).getOrientation(); // Not flagged } @@ -228,13 +228,13 @@ class ApiCallTest: Activity() { if (Build.VERSION.SDK_INT >= 16) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { - GridLayout(null); // Flagged + GridLayout(null); // Flagged } } @@ -424,13 +424,13 @@ class ApiCallTest: Activity() { val linear = layout as LinearLayout // OK API 1 } - abstract class ErrorVectorDravable : VectorDrawable(), Parcelable + abstract class ErrorVectorDravable : VectorDrawable(), Parcelable @TargetApi(21) class MyVectorDravable : VectorDrawable() fun testTypes() { - GridLayout(this) + GridLayout(this) val c = VectorDrawable::class.java } diff --git a/idea/testData/android/lint/apiCheck.kt.173 b/idea/testData/android/lint/apiCheck.kt.173 new file mode 100644 index 00000000000..fb8e110aaea --- /dev/null +++ b/idea/testData/android/lint/apiCheck.kt.173 @@ -0,0 +1,486 @@ + +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection + +import android.animation.RectEvaluator +import android.annotation.SuppressLint +import android.annotation.TargetApi +import org.w3c.dom.DOMError +import org.w3c.dom.DOMErrorHandler +import org.w3c.dom.DOMLocator + +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams +import android.app.Activity +import android.app.ApplicationErrorReport +import android.graphics.drawable.VectorDrawable +import android.graphics.Path +import android.graphics.PorterDuff +import android.graphics.Rect +import android.os.Build +import android.widget.* +import dalvik.bytecode.OpcodeInfo + +import android.os.Build.VERSION +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES +import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH +import android.os.Build.VERSION_CODES.JELLY_BEAN +import android.os.Bundle +import android.os.Parcelable +import android.system.ErrnoException +import android.widget.TextView + +@Suppress("SENSELESS_COMPARISON", "UNUSED_EXPRESSION", "UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION", "USELESS_CAST") +class ApiCallTest: Activity() { + + fun method(chronometer: Chronometer, locator: DOMLocator) { + chronometer.setBackground(null) + + // Ok + Bundle().getInt("") + + View.SYSTEM_UI_FLAG_FULLSCREEN + + // Virtual call + getActionBar() // API 11 + actionBar // API 11 + + // Class references (no call or field access) + val error: DOMError? = null // API 8 + val clz = DOMErrorHandler::class // API 8 + + // Method call + chronometer.onChronometerTickListener // API 3 + + // Inherited method call (from TextView + chronometer.setTextIsSelectable(true) // API 11 + + GridLayout::class + + // Field access + val field = OpcodeInfo.MAXIMUM_VALUE // API 11 + + + val fillParent = LayoutParams.FILL_PARENT // API 1 + // This is a final int, which means it gets inlined + val matchParent = LayoutParams.MATCH_PARENT // API 8 + // Field access: non final + val batteryInfo = report!!.batteryInfo + + // Enum access + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) { + val mode = PorterDuff.Mode.OVERLAY // API 11 + } + } + + fun test(rect: Rect) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + RectEvaluator(rect); // OK + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (rect != null) { + RectEvaluator(rect); // OK + } + } + } + + fun test2(rect: Rect) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + RectEvaluator(rect); // OK + } + } + + fun test3(rect: Rect) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + RectEvaluator(); // ERROR + } + } + + fun test4(rect: Rect) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + System.out.println("Something"); + RectEvaluator(rect); // OK + } else { + RectEvaluator(rect); // ERROR + } + } + + fun test5(rect: Rect) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) { + RectEvaluator(rect); // ERROR + } else { + RectEvaluator(rect); // ERROR + } + } + + fun test(priority: Boolean, layout: ViewGroup) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + + if (SDK_INT >= ICE_CREAM_SANDWICH) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + GridLayout(null).getOrientation(); // Flagged + } else { + GridLayout(null).getOrientation(); // Not flagged + } + + if (Build.VERSION.SDK_INT >= 14) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + + if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + + // Nested conditionals + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + if (priority) { + GridLayout(null).getOrientation(); // Flagged + } else { + GridLayout(null).getOrientation(); // Flagged + } + } else { + GridLayout(null).getOrientation(); // Flagged + } + + // Nested conditionals 2 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (priority) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null).getOrientation(); // Not flagged + } + } else { + GridLayout(null); // Flagged + } + } + + fun test2(priority: Boolean) { + if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + + if (android.os.Build.VERSION.SDK_INT >= 16) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + + if (android.os.Build.VERSION.SDK_INT >= 13) { + GridLayout(null).getOrientation(); // Flagged + } else { + GridLayout(null); // Flagged + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + + if (SDK_INT >= JELLY_BEAN) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + GridLayout(null); // Flagged + } else { + GridLayout(null).getOrientation(); // Not flagged + } + + if (Build.VERSION.SDK_INT >= 16) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + GridLayout(null).getOrientation(); // Not flagged + } else { + GridLayout(null); // Flagged + } + } + + fun test(textView: TextView) { + if (textView.isSuggestionsEnabled()) { + //ERROR + } + if (textView.isSuggestionsEnabled) { + //ERROR + } + + if (SDK_INT >= JELLY_BEAN && textView.isSuggestionsEnabled()) { + //NO ERROR + } + + if (SDK_INT >= JELLY_BEAN && textView.isSuggestionsEnabled) { + //NO ERROR + } + + if (SDK_INT >= JELLY_BEAN && (textView.text != "" || textView.isSuggestionsEnabled)) { + //NO ERROR + } + + if (SDK_INT < JELLY_BEAN && (textView.text != "" || textView.isSuggestionsEnabled)) { + //ERROR + } + + if (SDK_INT < JELLY_BEAN && textView.isSuggestionsEnabled()) { + //ERROR + } + + if (SDK_INT < JELLY_BEAN && textView.isSuggestionsEnabled) { + //ERROR + } + + if (SDK_INT < JELLY_BEAN || textView.isSuggestionsEnabled) { + //NO ERROR + } + + if (SDK_INT > JELLY_BEAN || textView.isSuggestionsEnabled) { + //ERROR + } + + + // getActionBar() API 11 + if (SDK_INT <= 10 || getActionBar() == null) { + //NO ERROR + } + + if (SDK_INT < 10 || getActionBar() == null) { + //ERROR + } + + if (SDK_INT < 11 || getActionBar() == null) { + //NO ERROR + } + + if (SDK_INT != 11 || getActionBar() == null) { + //NO ERROR + } + + if (SDK_INT != 12 || getActionBar() == null) { + //ERROR + } + + if (SDK_INT <= 11 || getActionBar() == null) { + //NO ERROR + } + + if (SDK_INT < 12 || getActionBar() == null) { + //NO ERROR + } + + if (SDK_INT <= 12 || getActionBar() == null) { + //NO ERROR + } + + if (SDK_INT < 9 || getActionBar() == null) { + //ERROR + } + + if (SDK_INT <= 9 || getActionBar() == null) { + //ERROR + } + } + + fun testReturn() { + if (SDK_INT < 11) { + return + } + + // No Error + val actionBar = getActionBar() + } + + fun testThrow() { + if (SDK_INT < 11) { + throw IllegalStateException() + } + + // No Error + val actionBar = getActionBar() + } + + fun testError() { + if (SDK_INT < 11) { + error("Api") + } + + // No Error + val actionBar = getActionBar() + } + + fun testWithoutAnnotation(textView: TextView) { + if (textView.isSuggestionsEnabled()) { + + } + + if (textView.isSuggestionsEnabled) { + + } + } + + @TargetApi(JELLY_BEAN) + fun testWithTargetApiAnnotation(textView: TextView) { + if (textView.isSuggestionsEnabled()) { + //NO ERROR, annotation + } + + if (textView.isSuggestionsEnabled) { + //NO ERROR, annotation + } + } + + @SuppressLint("NewApi") + fun testWithSuppressLintAnnotation(textView: TextView) { + if (textView.isSuggestionsEnabled()) { + //NO ERROR, annotation + } + + if (textView.isSuggestionsEnabled) { + //NO ERROR, annotation + } + } + + fun testCatch() { + try { + + } catch (e: ErrnoException) { + + } + } + + fun testOverload() { + // this overloaded addOval available only on API Level 21 + Path().addOval(0f, 0f, 0f, 0f, Path.Direction.CW) + } + + // KT-14737 False error with short-circuit evaluation + fun testShortCircuitEvaluation() { + getDrawable(0) // error here as expected + if(Build.VERSION.SDK_INT >= 23 + && null == getDrawable(0)) // error here should not occur + { + getDrawable(0) // no error here as expected + } + } + + // KT-1482 Kotlin Lint: "Calling new methods on older versions" does not report call on receiver in extension function + private fun Bundle.caseE1a() { getBinder("") } + + private fun Bundle.caseE1c() { this.getBinder("") } + + private fun caseE1b(bundle: Bundle) { bundle.getBinder("") } + + // KT-12023 Kotlin Lint: Cast doesn't trigger minSdk error + fun testCast(layout: ViewGroup) { + if (layout is LinearLayout) {} // OK API 1 + layout as? LinearLayout // OK API 1 + layout as LinearLayout // OK API 1 + + if (layout !is GridLayout) {} + layout as? GridLayout + layout as GridLayout + + val grid = layout as? GridLayout + val linear = layout as LinearLayout // OK API 1 + } + + abstract class ErrorVectorDravable : VectorDrawable(), Parcelable + + @TargetApi(21) + class MyVectorDravable : VectorDrawable() + + fun testTypes() { + GridLayout(this) + val c = VectorDrawable::class.java + } + + fun testCallWithApiAnnotation(textView: TextView) { + MyVectorDravable() + testWithTargetApiAnnotation(textView) + } + + companion object : Activity() { + fun test() { + getDrawable(0) + } + } + + // Return type + internal // API 14 + val gridLayout: GridLayout? + get() = null + + private val report: ApplicationErrorReport? + get() = null +} + +object O: Activity() { + fun test() { + getDrawable(0) + } +} + +fun testJava8() { + // Error, Api 24, Java8 + mutableListOf(1, 2, 3).removeIf { + true + } + + // Ok, Kotlin + mutableListOf(1, 2, 3).removeAll { + true + } + + // Error, Api 24, Java8 + mapOf(1 to 2).forEach { key, value -> key + value } + + // Ok, Kotlin + mapOf(1 to 2).forEach { (key, value) -> key + value } +} + +interface WithDefault { + // Should be ok + fun methodWithBody() { + return + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/callSuper.kt b/idea/testData/android/lint/callSuper.kt index df30cfe7670..8f7fd188881 100644 --- a/idea/testData/android/lint/callSuper.kt +++ b/idea/testData/android/lint/callSuper.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintMissingSuperCallInspection package android.support.annotation diff --git a/idea/testData/android/lint/callSuper.kt.173 b/idea/testData/android/lint/callSuper.kt.173 new file mode 100644 index 00000000000..df30cfe7670 --- /dev/null +++ b/idea/testData/android/lint/callSuper.kt.173 @@ -0,0 +1,97 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection + +package android.support.annotation + +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FUNCTION) +annotation class CallSuper + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class CallSuperTest { + private class Child : Parent() { + override fun test1() { + // ERROR + } + + override fun test2() { + // ERROR + } + + override fun test3() { + // ERROR + } + + override fun test4(arg: Int) { + // ERROR + } + + override fun test4(arg: String) { + // OK + } + + + override fun test5(arg1: Int, arg2: Boolean, arg3: Map, *>, // ERROR + arg4: Array, vararg arg5: Int) { + } + + override fun test5() { + // ERROR + super.test6() // (wrong super) + } + + override fun test6() { + // OK + val x = 5 + super.test6() + System.out.println(x) + } + } + + private open class Parent : ParentParent() { + @CallSuper + protected open fun test1() { + } + + override fun test3() { + super.test3() + } + + @CallSuper + protected open fun test4(arg: Int) { + } + + protected open fun test4(arg: String) { + } + + @CallSuper + protected open fun test5() { + } + + @CallSuper + protected open fun test5(arg1: Int, arg2: Boolean, arg3: Map, *>, + arg4: Array, vararg arg5: Int) { + } + } + + private open class ParentParent : ParentParentParent() { + @CallSuper + protected open fun test2() { + } + + @CallSuper + protected open fun test3() { + } + + @CallSuper + protected open fun test6() { + } + + @CallSuper + protected fun test7() { + } + + + } + + private open class ParentParentParent +} diff --git a/idea/testData/android/lint/closeCursor.kt b/idea/testData/android/lint/closeCursor.kt index ca26201fd01..82a01cd75cf 100644 --- a/idea/testData/android/lint/closeCursor.kt +++ b/idea/testData/android/lint/closeCursor.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintRecycleInspection @file:Suppress("UNUSED_VARIABLE") diff --git a/idea/testData/android/lint/closeCursor.kt.173 b/idea/testData/android/lint/closeCursor.kt.173 new file mode 100644 index 00000000000..ca26201fd01 --- /dev/null +++ b/idea/testData/android/lint/closeCursor.kt.173 @@ -0,0 +1,36 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection + +@file:Suppress("UNUSED_VARIABLE") + +import android.app.Activity +import android.os.Bundle + +class MainActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val cursor = contentResolver.query(null, null, null, null, null) + + // WARNING + contentResolver.query(null, null, null, null, null) + + // OK, closed in chained call + contentResolver.query(null, null, null, null, null).close() + + // KT-14677: Kotlin Lint: "Missing recycle() calls" report cursor with `use()` call + val cursorUsed = contentResolver.query(null, null, null, null, null) + cursorUsed.use { } + + // OK, used in chained call + contentResolver.query(null, null, null, null, null).use { + + } + + // KT-13372: Android Lint for Kotlin: false positive "Cursor should be freed" inside 'if' expression + if (true) { + val c = contentResolver.query(null, null, null, null, null) + c.close() + } + } +} diff --git a/idea/testData/android/lint/commitFragment.kt b/idea/testData/android/lint/commitFragment.kt index fc6f33303dc..25ad12e680c 100644 --- a/idea/testData/android/lint/commitFragment.kt +++ b/idea/testData/android/lint/commitFragment.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintCommitTransactionInspection @file:Suppress("UNUSED_VARIABLE") diff --git a/idea/testData/android/lint/commitFragment.kt.173 b/idea/testData/android/lint/commitFragment.kt.173 new file mode 100644 index 00000000000..fc6f33303dc --- /dev/null +++ b/idea/testData/android/lint/commitFragment.kt.173 @@ -0,0 +1,41 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection + +@file:Suppress("UNUSED_VARIABLE") + +import android.app.Activity +import android.app.FragmentTransaction +import android.app.FragmentManager +import android.os.Bundle + +class MainActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + //OK + val transaction = fragmentManager.beginTransaction() + val transaction2: FragmentTransaction + transaction2 = fragmentManager.beginTransaction() + transaction.commit() + transaction2.commit() + + //WARNING + val transaction3 = fragmentManager.beginTransaction() + + //OK + fragmentManager.beginTransaction().commit() + fragmentManager.beginTransaction().add(null, "A").commit() + + //OK KT-14470 + Runnable { + val a = fragmentManager.beginTransaction() + a.commit() + } + } + + // KT-14780: Kotlin Lint: "Missing commit() calls" false positive when the result of `commit()` is assigned or used as receiver + fun testResultOfCommit(fm: FragmentManager) { + val r1 = fm.beginTransaction().hide(fm.findFragmentByTag("aTag")).commit() + val r2 = fm.beginTransaction().hide(fm.findFragmentByTag("aTag")).commit().toString() + } +} diff --git a/idea/testData/android/lint/javaPerformance.kt b/idea/testData/android/lint/javaPerformance.kt index b3cddced1f8..0bf3b5f0957 100644 --- a/idea/testData/android/lint/javaPerformance.kt +++ b/idea/testData/android/lint/javaPerformance.kt @@ -1,6 +1,6 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection -// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection -// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintDrawAllocationInspection +// INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintUseSparseArraysInspection +// INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintUseValueOfInspection import android.annotation.SuppressLint import java.util.HashMap @@ -23,8 +23,8 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) super.onDraw(canvas) // Various allocations: - java.lang.String("foo") - val s = java.lang.String("bar") + java.lang.String("foo") + val s = java.lang.String("bar") // This one should not be reported: @SuppressLint("DrawAllocation") @@ -69,11 +69,11 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) // Should use SparseBooleanArray val myBoolMap = HashMap() // Should use SparseIntArray - val myIntMap = java.util.HashMap() + val myIntMap = java.util.HashMap() // This one should not be reported: @SuppressLint("UseSparseArrays") - val myOtherMap = HashMap() + val myOtherMap = HashMap() } protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int, @@ -100,20 +100,20 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - java.lang.String("flag me") + java.lang.String("flag me") } @SuppressWarnings("null") // not real code override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - java.lang.String("flag me") + java.lang.String("flag me") // Forbidden factory methods: - Bitmap.createBitmap(100, 100, null) - android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false) - BitmapFactory.decodeFile(null) + Bitmap.createBitmap(100, 100, null) + android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false) + BitmapFactory.decodeFile(null) val canvas: Canvas? = null - canvas!!.getClipBounds() // allocates on your behalf - canvas.clipBounds // allocates on your behalf + canvas!!.getClipBounds() // allocates on your behalf + canvas.clipBounds // allocates on your behalf canvas.getClipBounds(null) // NOT an error val layoutWidth = width @@ -137,7 +137,7 @@ class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) // NOT lazy initialization if (!initialized || mOverlay == null) { - java.lang.String("foo") + java.lang.String("foo") } } diff --git a/idea/testData/android/lint/javaPerformance.kt.173 b/idea/testData/android/lint/javaPerformance.kt.173 new file mode 100644 index 00000000000..b3cddced1f8 --- /dev/null +++ b/idea/testData/android/lint/javaPerformance.kt.173 @@ -0,0 +1,193 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection +// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection +// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection + +import android.annotation.SuppressLint +import java.util.HashMap +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.util.SparseArray +import android.widget.Button + +@SuppressWarnings("unused") +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) : Button(context, attrs, defStyle) { + + private var cachedRect: Rect? = null + private var shader: LinearGradient? = null + private var lastHeight: Int = 0 + private var lastWidth: Int = 0 + + override fun onDraw(canvas: android.graphics.Canvas) { + super.onDraw(canvas) + + // Various allocations: + java.lang.String("foo") + val s = java.lang.String("bar") + + // This one should not be reported: + @SuppressLint("DrawAllocation") + val i = 5 + + // Cached object initialized lazily: should not complain about these + if (cachedRect == null) { + cachedRect = Rect(0, 0, 100, 100) + } + if (cachedRect == null || cachedRect!!.width() != 50) { + cachedRect = Rect(0, 0, 50, 100) + } + + val b = java.lang.Boolean.valueOf(true)!! // auto-boxing + dummy(1, 2) + + // Non-allocations + super.animate() + dummy2(1, 2) + + // This will involve allocations, but we don't track + // inter-procedural stuff here + someOtherMethod() + } + + internal fun dummy(foo: Int?, bar: Int) { + dummy2(foo!!, bar) + } + + internal fun dummy2(foo: Int, bar: Int) { + } + + internal fun someOtherMethod() { + // Allocations are okay here + java.lang.String("foo") + val s = java.lang.String("bar") + val b = java.lang.Boolean.valueOf(true)!! // auto-boxing + + + // Sparse array candidates + val myMap = HashMap() + // Should use SparseBooleanArray + val myBoolMap = HashMap() + // Should use SparseIntArray + val myIntMap = java.util.HashMap() + + // This one should not be reported: + @SuppressLint("UseSparseArrays") + val myOtherMap = HashMap() + } + + protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int, + x: Boolean) { + // wrong signature + java.lang.String("not an error") + } + + protected fun onMeasure(widthMeasureSpec: Int) { + // wrong signature + java.lang.String("not an error") + } + + protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, + bottom: Int, wrong: Int) { + // wrong signature + java.lang.String("not an error") + } + + protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int) { + // wrong signature + java.lang.String("not an error") + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, + bottom: Int) { + java.lang.String("flag me") + } + + @SuppressWarnings("null") // not real code + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + java.lang.String("flag me") + + // Forbidden factory methods: + Bitmap.createBitmap(100, 100, null) + android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false) + BitmapFactory.decodeFile(null) + val canvas: Canvas? = null + canvas!!.getClipBounds() // allocates on your behalf + canvas.clipBounds // allocates on your behalf + canvas.getClipBounds(null) // NOT an error + + val layoutWidth = width + val layoutHeight = height + if (mAllowCrop && (mOverlay == null || mOverlay!!.width != layoutWidth || + mOverlay!!.height != layoutHeight)) { + mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888) + mOverlayCanvas = Canvas(mOverlay!!) + } + + if (widthMeasureSpec == 42) { + throw IllegalStateException("Test") // NOT an allocation + } + + // More lazy init tests + var initialized = false + if (!initialized) { + java.lang.String("foo") + initialized = true + } + + // NOT lazy initialization + if (!initialized || mOverlay == null) { + java.lang.String("foo") + } + } + + internal fun factories() { + val i1 = 42 + val l1 = 42L + val b1 = true + val c1 = 'c' + val f1 = 1.0f + val d1 = 1.0 + + // The following should not generate errors: + val i3 = Integer.valueOf(42) + } + + private val mAllowCrop: Boolean = false + private var mOverlayCanvas: Canvas? = null + private var mOverlay: Bitmap? = null + + override fun layout(l: Int, t: Int, r: Int, b: Int) { + // Using "this." to reference fields + if (this.shader == null) + this.shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null, + Shader.TileMode.REPEAT) + + val width = width + val height = height + + if (shader == null || lastWidth != width || lastHeight != height) { + lastWidth = width + lastHeight = height + + shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null, Shader.TileMode.REPEAT) + } + } + + fun inefficientSparseArray() { + SparseArray() // Use SparseIntArray instead + SparseArray() // Use SparseLongArray instead + SparseArray() // Use SparseBooleanArray instead + SparseArray() // OK + } + + fun longSparseArray() { + // but only minSdkVersion >= 17 or if has v4 support lib + val myStringMap = HashMap() + } + + fun byteSparseArray() { + // bytes easily apply to ints + val myByteMap = HashMap() + } +} diff --git a/idea/testData/android/lint/javaScriptInterface.kt b/idea/testData/android/lint/javaScriptInterface.kt index 8d425594a56..cc916fb41cb 100644 --- a/idea/testData/android/lint/javaScriptInterface.kt +++ b/idea/testData/android/lint/javaScriptInterface.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintJavascriptInterfaceInspection import android.annotation.SuppressLint import android.webkit.JavascriptInterface diff --git a/idea/testData/android/lint/javaScriptInterface.kt.173 b/idea/testData/android/lint/javaScriptInterface.kt.173 new file mode 100644 index 00000000000..8d425594a56 --- /dev/null +++ b/idea/testData/android/lint/javaScriptInterface.kt.173 @@ -0,0 +1,66 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection + +import android.annotation.SuppressLint +import android.webkit.JavascriptInterface +import android.webkit.WebView + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "UNUSED_VALUE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class JavaScriptTestK { + fun test(webview: WebView) { + webview.addJavascriptInterface(AnnotatedObject(), "myobj") + + webview.addJavascriptInterface(InheritsFromAnnotated(), "myobj") + webview.addJavascriptInterface(NonAnnotatedObject(), "myobj") + + webview.addJavascriptInterface(null, "nothing") + webview.addJavascriptInterface(object : Any() { @JavascriptInterface fun method() {} }, "nothing") + webview.addJavascriptInterface(JavascriptFace(), "nothing") + + var o: Any = NonAnnotatedObject() + webview.addJavascriptInterface(o, "myobj") + o = InheritsFromAnnotated() + webview.addJavascriptInterface(o, "myobj") + } + + fun test(webview: WebView, object1: AnnotatedObject, object2: NonAnnotatedObject) { + webview.addJavascriptInterface(object1, "myobj") + webview.addJavascriptInterface(object2, "myobj") + } + + @SuppressLint("JavascriptInterface") + fun testSuppressed(webview: WebView) { + webview.addJavascriptInterface(NonAnnotatedObject(), "myobj") + } + + fun testLaterReassignment(webview: WebView) { + var o: Any = NonAnnotatedObject() + val t = o + webview.addJavascriptInterface(t, "myobj") + o = AnnotatedObject() + } + + class NonAnnotatedObject() { + fun test1() {} + fun test2() {} + } + + open class AnnotatedObject { + @JavascriptInterface + open fun test1() {} + + open fun test2() {} + + @JavascriptInterface + fun test3() {} + } + + class InheritsFromAnnotated : AnnotatedObject() { + override fun test1() {} + override fun test2() {} + } + +} + +class JavascriptFace { + fun method() {} +} \ No newline at end of file diff --git a/idea/testData/android/lint/layoutInflation.kt b/idea/testData/android/lint/layoutInflation.kt index 65bd1e77cf5..dbe0461ede5 100644 --- a/idea/testData/android/lint/layoutInflation.kt +++ b/idea/testData/android/lint/layoutInflation.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInflateParamsInspection import android.view.LayoutInflater import android.view.View diff --git a/idea/testData/android/lint/layoutInflation.kt.173 b/idea/testData/android/lint/layoutInflation.kt.173 new file mode 100644 index 00000000000..65bd1e77cf5 --- /dev/null +++ b/idea/testData/android/lint/layoutInflation.kt.173 @@ -0,0 +1,50 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +abstract class LayoutInflationTest : BaseAdapter() { + lateinit var mInflater: LayoutInflater + + override fun getView(position: Int, convertView: View, parent: ViewGroup): View { + var view = convertView + view = mInflater.inflate(R.layout.your_layout, null) + view = mInflater.inflate(R.layout.your_layout, null, true) + view = mInflater.inflate(R.layout.your_layout, parent) + view = WeirdInflater.inflate(view, null) + + return view + } + + object WeirdInflater { + fun inflate(view: View, parent: View?) = view + } + + object R { + object layout { + val your_layout = 1 + } + } +} + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +abstract class LayoutInflationTest2 : BaseAdapter() { + lateinit var mInflater: LayoutInflater + + override fun getView(position: Int, convertView: View, parent: ViewGroup): View? { + return if (true) { + mInflater.inflate(R.layout.your_layout, parent) + } else { + null + } + } + + object R { + object layout { + val your_layout = 1 + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/log.kt b/idea/testData/android/lint/log.kt index cc9e5e959c2..f82e52b3dab 100644 --- a/idea/testData/android/lint/log.kt +++ b/idea/testData/android/lint/log.kt @@ -1,6 +1,6 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection -// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection -// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintLogConditionalInspection +// INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintLogTagMismatchInspection +// INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintLongLogTagInspection import android.annotation.SuppressLint import android.util.Log @@ -15,8 +15,8 @@ class LogTest { Log.d(TAG1, m) // ok: unconditional, but not performing computation Log.d(TAG1, "a" + "b") // ok: unconditional, but not performing non-constant computation Log.d(TAG1, Constants.MY_MESSAGE) // ok: unconditional, but constant string - Log.i(TAG1, "message" + m) // error: unconditional w/ computation - Log.i(TAG1, toString()) // error: unconditional w/ computation + Log.i(TAG1, "message" + m) // error: unconditional w/ computation + Log.i(TAG1, toString()) // error: unconditional w/ computation Log.e(TAG1, toString()) // ok: only flagging debug/info messages Log.w(TAG1, toString()) // ok: only flagging debug/info messages Log.wtf(TAG1, toString()) // ok: only flagging debug/info messages @@ -44,22 +44,22 @@ class LogTest { if (shouldLog) { // String literal tags Log.d("short_tag", "message") // ok: short - Log.d("really_really_really_really_really_long_tag", "message") // error: too long + Log.d("really_really_really_really_really_long_tag", "message") // error: too long // Resolved field tags Log.d(TAG1, "message") // ok: short Log.d(TAG22, "message") // ok: short Log.d(TAG23, "message") // ok: threshold - Log.d(TAG24, "message") // error: too long - Log.d(LONG_TAG, "message") // error: way too long + Log.d(TAG24, "message") // error: too long + Log.d(LONG_TAG, "message") // error: way too long // Locally defined variable tags val LOCAL_TAG = "MyReallyReallyReallyReallyReallyLongTag" - Log.d(LOCAL_TAG, "message") // error: too long + Log.d(LOCAL_TAG, "message") // error: too long // Concatenated tags - Log.d(TAG22 + TAG1, "message") // error: too long - Log.d(TAG22 + "MyTag", "message") // error: too long + Log.d(TAG22 + TAG1, "message") // error: too long + Log.d(TAG22 + "MyTag", "message") // error: too long } } diff --git a/idea/testData/android/lint/log.kt.173 b/idea/testData/android/lint/log.kt.173 new file mode 100644 index 00000000000..cc9e5e959c2 --- /dev/null +++ b/idea/testData/android/lint/log.kt.173 @@ -0,0 +1,111 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection +// INSPECTION_CLASS2: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection +// INSPECTION_CLASS3: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection + +import android.annotation.SuppressLint +import android.util.Log +import android.util.Log.DEBUG + +@SuppressWarnings("UnusedDeclaration") +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class LogTest { + + fun checkConditional(m: String) { + Log.d(TAG1, "message") // ok: unconditional, but not performing computation + Log.d(TAG1, m) // ok: unconditional, but not performing computation + Log.d(TAG1, "a" + "b") // ok: unconditional, but not performing non-constant computation + Log.d(TAG1, Constants.MY_MESSAGE) // ok: unconditional, but constant string + Log.i(TAG1, "message" + m) // error: unconditional w/ computation + Log.i(TAG1, toString()) // error: unconditional w/ computation + Log.e(TAG1, toString()) // ok: only flagging debug/info messages + Log.w(TAG1, toString()) // ok: only flagging debug/info messages + Log.wtf(TAG1, toString()) // ok: only flagging debug/info messages + if (Log.isLoggable(TAG1, 0)) { + Log.d(TAG1, toString()) // ok: conditional + } + } + + fun checkWrongTag(tag: String) { + if (Log.isLoggable(TAG1, Log.DEBUG)) { + Log.d(TAG2, "message") // warn: mismatched tags! + } + if (Log.isLoggable("my_tag", Log.DEBUG)) { + Log.d("other_tag", "message") // warn: mismatched tags! + } + if (Log.isLoggable("my_tag", Log.DEBUG)) { + Log.d("my_tag", "message") // ok: strings equal + } + if (Log.isLoggable(tag, Log.DEBUG)) { + Log.d(tag, "message") // ok: same variable + } + } + + fun checkLongTag(shouldLog: Boolean) { + if (shouldLog) { + // String literal tags + Log.d("short_tag", "message") // ok: short + Log.d("really_really_really_really_really_long_tag", "message") // error: too long + + // Resolved field tags + Log.d(TAG1, "message") // ok: short + Log.d(TAG22, "message") // ok: short + Log.d(TAG23, "message") // ok: threshold + Log.d(TAG24, "message") // error: too long + Log.d(LONG_TAG, "message") // error: way too long + + // Locally defined variable tags + val LOCAL_TAG = "MyReallyReallyReallyReallyReallyLongTag" + Log.d(LOCAL_TAG, "message") // error: too long + + // Concatenated tags + Log.d(TAG22 + TAG1, "message") // error: too long + Log.d(TAG22 + "MyTag", "message") // error: too long + } + } + + fun checkWrongLevel(tag: String) { + if (Log.isLoggable(TAG1, Log.DEBUG)) { + Log.d(TAG1, "message") // ok: right level + } + if (Log.isLoggable(TAG1, Log.INFO)) { + Log.i(TAG1, "message") // ok: right level + } + if (Log.isLoggable(TAG1, Log.DEBUG)) { + Log.v(TAG1, "message") // warn: wrong level + } + if (Log.isLoggable(TAG1, DEBUG)) { + // static import of level + Log.v(TAG1, "message") // warn: wrong level + } + if (Log.isLoggable(TAG1, Log.VERBOSE)) { + Log.d(TAG1, "message") // warn? verbose is a lower logging level, which includes debug + } + if (Log.isLoggable(TAG1, Constants.MY_LEVEL)) { + Log.d(TAG1, "message") // ok: unknown level alias + } + } + + @SuppressLint("all") + fun suppressed1() { + Log.d(TAG1, "message") // ok: suppressed + } + + @SuppressLint("LogConditional") + fun suppressed2() { + Log.d(TAG1, "message") // ok: suppressed + } + + private object Constants { + val MY_MESSAGE = "My Message" + val MY_LEVEL = 5 + } + + companion object { + private val TAG1 = "MyTag1" + private val TAG2 = "MyTag2" + private val TAG22 = "1234567890123456789012" + private val TAG23 = "12345678901234567890123" + private val TAG24 = "123456789012345678901234" + private val LONG_TAG = "MyReallyReallyReallyReallyReallyLongTag" + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/noInternationalSms.kt b/idea/testData/android/lint/noInternationalSms.kt index 31fecc25918..3f26bafc201 100644 --- a/idea/testData/android/lint/noInternationalSms.kt +++ b/idea/testData/android/lint/noInternationalSms.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintUnlocalizedSmsInspection import android.content.Context import android.telephony.SmsManager diff --git a/idea/testData/android/lint/noInternationalSms.kt.173 b/idea/testData/android/lint/noInternationalSms.kt.173 new file mode 100644 index 00000000000..31fecc25918 --- /dev/null +++ b/idea/testData/android/lint/noInternationalSms.kt.173 @@ -0,0 +1,19 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection + +import android.content.Context +import android.telephony.SmsManager + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class NonInternationalizedSmsDetectorTest { + private fun sendLocalizedMessage(context: Context) { + // Don't warn here + val sms = SmsManager.getDefault() + sms.sendTextMessage("+1234567890", null, null, null, null) + } + + private fun sendAlternativeCountryPrefix(context: Context) { + // Do warn here + val sms = SmsManager.getDefault() + sms.sendMultipartTextMessage("001234567890", null, null, null, null) + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/overrideConcrete.kt b/idea/testData/android/lint/overrideConcrete.kt index 55a5bb3c228..278250137cf 100644 --- a/idea/testData/android/lint/overrideConcrete.kt +++ b/idea/testData/android/lint/overrideConcrete.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintOverrideAbstractInspection import android.annotation.SuppressLint import android.annotation.TargetApi diff --git a/idea/testData/android/lint/overrideConcrete.kt.173 b/idea/testData/android/lint/overrideConcrete.kt.173 new file mode 100644 index 00000000000..55a5bb3c228 --- /dev/null +++ b/idea/testData/android/lint/overrideConcrete.kt.173 @@ -0,0 +1,61 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection + +import android.annotation.SuppressLint +import android.annotation.TargetApi +import android.os.Build +import android.service.notification.NotificationListenerService +import android.service.notification.StatusBarNotification + +@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class OverrideConcreteTest2 { + // OK: This one specifies both methods + private open class MyNotificationListenerService1 : NotificationListenerService() { + override fun onNotificationPosted(statusBarNotification: StatusBarNotification) { + } + + override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) { + } + } + + // Error: Misses onNotificationPosted + private class MyNotificationListenerService2 : NotificationListenerService() { + override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) { + } + } + + // Error: Misses onNotificationRemoved + private open class MyNotificationListenerService3 : NotificationListenerService() { + override fun onNotificationPosted(statusBarNotification: StatusBarNotification) { + } + } + + // Error: Missing both; wrong signatures (first has wrong arg count, second has wrong type) + private class MyNotificationListenerService4 : NotificationListenerService() { + fun onNotificationPosted(statusBarNotification: StatusBarNotification, flags: Int) { + } + + fun onNotificationRemoved(statusBarNotification: Int) { + } + } + + // OK: Inherits from a class which define both + private class MyNotificationListenerService5 : MyNotificationListenerService1() + + // OK: Inherits from a class which defines only one, but the other one is defined here + private class MyNotificationListenerService6 : MyNotificationListenerService3() { + override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) { + } + } + + // Error: Inheriting from a class which only defines one + private class MyNotificationListenerService7 : MyNotificationListenerService3() + + // OK: Has target api setting a local version that is high enough + @TargetApi(21) + private class MyNotificationListenerService8 : NotificationListenerService() + + // OK: Suppressed + @SuppressLint("OverrideAbstract") + private class MyNotificationListenerService9 : MyNotificationListenerService1() +} \ No newline at end of file diff --git a/idea/testData/android/lint/parcel.kt b/idea/testData/android/lint/parcel.kt index 84e3e4faba6..3021ded2603 100644 --- a/idea/testData/android/lint/parcel.kt +++ b/idea/testData/android/lint/parcel.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection @file:Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") import android.os.Parcel diff --git a/idea/testData/android/lint/parcel.kt.173 b/idea/testData/android/lint/parcel.kt.173 new file mode 100644 index 00000000000..84e3e4faba6 --- /dev/null +++ b/idea/testData/android/lint/parcel.kt.173 @@ -0,0 +1,104 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection + +@file:Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +import android.os.Parcel +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +class MyParcelable1 : Parcelable { + override fun describeContents() = 0 + override fun writeToParcel(arg0: Parcel, arg1: Int) {} +} + +internal class MyParcelable2 : Parcelable { + override fun describeContents() = 0 + + override fun writeToParcel(arg0: Parcel, arg1: Int) {} + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun newArray(size: Int) = null!! + override fun createFromParcel(source: Parcel?) = null!! + } + } +} + +internal class MyParcelable3 : Parcelable { + override fun describeContents() = 0 + override fun writeToParcel(arg0: Parcel, arg1: Int) {} + + companion object { + @JvmField + val CREATOR = 0 // Wrong type + } +} + +class RecyclerViewScrollPosition(val position: Int, val topOffset: Int): Parcelable { + override fun describeContents(): Int = 0 + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(position) + dest.writeInt(topOffset) + } + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): RecyclerViewScrollPosition { + val position = parcel.readInt() + val topOffset = parcel.readInt() + return RecyclerViewScrollPosition(position, topOffset) + } + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + + } +} + +class RecyclerViewScrollPositionWithoutJvmF(val position: Int, val topOffset: Int): Parcelable { + override fun describeContents(): Int = 0 + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(position) + dest.writeInt(topOffset) + } + + companion object { + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): RecyclerViewScrollPosition { + val position = parcel.readInt() + val topOffset = parcel.readInt() + return RecyclerViewScrollPosition(position, topOffset) + } + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + + } +} + +class RecyclerViewScrollPosition2(val position: Int, val topOffset: Int): Parcelable { + override fun describeContents(): Int = 0 + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(position) + dest.writeInt(topOffset) + } + + companion object CREATOR: Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): RecyclerViewScrollPosition { + val position = parcel.readInt() + val topOffset = parcel.readInt() + return RecyclerViewScrollPosition(position, topOffset) + } + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } +} + +internal abstract class MyParcelable4 : Parcelable { + override fun describeContents() = 0 + override fun writeToParcel(arg0: Parcel, arg1: Int) {} +} + +@Parcelize +class ParcelizeUser(val firstName: String, val lastName: String) : Parcelable \ No newline at end of file diff --git a/idea/testData/android/lint/sdCardTest.kt b/idea/testData/android/lint/sdCardTest.kt index ffc534f68bd..f62ba6fc6e4 100644 --- a/idea/testData/android/lint/sdCardTest.kt +++ b/idea/testData/android/lint/sdCardTest.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection import java.io.File import android.content.Intent diff --git a/idea/testData/android/lint/sdCardTest.kt.173 b/idea/testData/android/lint/sdCardTest.kt.173 new file mode 100644 index 00000000000..ffc534f68bd --- /dev/null +++ b/idea/testData/android/lint/sdCardTest.kt.173 @@ -0,0 +1,45 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +import java.io.File +import android.content.Intent +import android.net.Uri + +/** + * Ignore comments - create("/sdcard/foo") + */ +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class SdCardTest { + internal var deviceDir = File("/sdcard/vr") + + init { + if (PROFILE_STARTUP) { + android.os.Debug.startMethodTracing("/sdcard/launcher") + } + + if (File("/sdcard").exists()) { + } + val FilePath = "/sdcard/" + File("test") + System.setProperty("foo.bar", "file://sdcard") + + + val intent = Intent(Intent.ACTION_PICK) + intent.setDataAndType(Uri.parse("file://sdcard/foo.json"), "application/bar-json") + intent.putExtra("path-filter", "/sdcard(/.+)*") + intent.putExtra("start-dir", "/sdcard") + val mypath = "/data/data/foo" + val base = "/data/data/foo.bar/test-profiling" + val s = "file://sdcard/foo" + + val sdCardPath by lazy { "/sdcard" } + fun localPropertyTest() { + val sdCardPathLocal by lazy { "/sdcard" } + } +} + +companion object { + private val PROFILE_STARTUP = true + private val SDCARD_TEST_HTML = "/sdcard/test.html" + val SDCARD_ROOT = "/sdcard" + val PACKAGES_PATH = "/sdcard/o/packages/" +} +} diff --git a/idea/testData/android/lint/setJavaScriptEnabled.kt b/idea/testData/android/lint/setJavaScriptEnabled.kt index 2c115572e2a..ed3a506e719 100644 --- a/idea/testData/android/lint/setJavaScriptEnabled.kt +++ b/idea/testData/android/lint/setJavaScriptEnabled.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSetJavaScriptEnabledInspection import android.annotation.SuppressLint import android.app.Activity @@ -8,8 +8,8 @@ import android.webkit.WebView public class HelloWebApp : Activity() { fun test(webView: WebView) { - webView.settings.javaScriptEnabled = true // bad - webView.getSettings().setJavaScriptEnabled(true) // bad + webView.settings.javaScriptEnabled = true // bad + webView.getSettings().setJavaScriptEnabled(true) // bad webView.getSettings().setJavaScriptEnabled(false) // good webView.loadUrl("file:///android_asset/www/index.html") } diff --git a/idea/testData/android/lint/setJavaScriptEnabled.kt.173 b/idea/testData/android/lint/setJavaScriptEnabled.kt.173 new file mode 100644 index 00000000000..2c115572e2a --- /dev/null +++ b/idea/testData/android/lint/setJavaScriptEnabled.kt.173 @@ -0,0 +1,24 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection + +import android.annotation.SuppressLint +import android.app.Activity +import android.webkit.WebView + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +public class HelloWebApp : Activity() { + + fun test(webView: WebView) { + webView.settings.javaScriptEnabled = true // bad + webView.getSettings().setJavaScriptEnabled(true) // bad + webView.getSettings().setJavaScriptEnabled(false) // good + webView.loadUrl("file:///android_asset/www/index.html") + } + + @SuppressLint("SetJavaScriptEnabled") + fun suppressed(webView: WebView) { + webView.getSettings().javaScriptEnabled = true; // bad + webView.getSettings().setJavaScriptEnabled(true) // bad + webView.getSettings().setJavaScriptEnabled(false); // good + webView.loadUrl("file:///android_asset/www/index.html"); + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/sharedPrefs.kt b/idea/testData/android/lint/sharedPrefs.kt index 790c9fd9310..023c04d7040 100644 --- a/idea/testData/android/lint/sharedPrefs.kt +++ b/idea/testData/android/lint/sharedPrefs.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintCommitPrefEditsInspection import android.app.Activity import android.content.Context @@ -29,35 +29,6 @@ class SharedPrefsText(context: Context) : Activity() { } } - // OK using with lambda - fun withLambda() { - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - with(preferences.edit()) { - putString("foo", "bar") - putInt("bar", 42) - apply() - } - } - - // OK using apply lambda - fun testApplyLambda() { - PreferenceManager.getDefaultSharedPreferences(this).edit().apply { - putString("foo", "bar") - putInt("bar", 42) - apply() - } - } - - // OK using also lambda - fun testAlsoLambda() { - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - preferences.edit().also { - it.putString("foo", "bar") - it.putInt("bar", 42) - it.apply() - } - } - // Not a bug fun test(foo: Foo) { val bar1 = foo.edit() @@ -85,43 +56,19 @@ class SharedPrefsText(context: Context) : Activity() { fun bug1(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) val preferences = PreferenceManager.getDefaultSharedPreferences(this) - val editor = preferences.edit() + val editor = preferences.edit() editor.putString("foo", "bar") editor.putInt("bar", 42) } - // Bug missing commit in apply lambda - fun applyLambdaMissingCommit() { - PreferenceManager.getDefaultSharedPreferences(this).edit().apply { - putString("foo", "bar") - putInt("bar", 42) - } - } - - // Bug missing commit in also lambda - fun alsoLambdaMissingCommit() { - PreferenceManager.getDefaultSharedPreferences(this).edit().also { - it.putString("foo", "bar") - it.putInt("bar", 42) - } - } - - // Bug missing commit in with lambda - fun withLambdaMissingCommit() { - with(PreferenceManager.getDefaultSharedPreferences(this).edit()) { - putString("foo", "bar") - putInt("bar", 42) - } - } - init { val preferences = PreferenceManager.getDefaultSharedPreferences(context) - val editor = preferences.edit() + val editor = preferences.edit() editor.putString("foo", "bar") } fun testResultOfCommit() { val r1 = PreferenceManager.getDefaultSharedPreferences(this).edit().putString("wat", "wat").commit() - val r2 = PreferenceManager.getDefaultSharedPreferences(this).edit().putString("wat", "wat").commit().toString() + val r2 = PreferenceManager.getDefaultSharedPreferences(this).edit().putString("wat", "wat").commit().toString() } } \ No newline at end of file diff --git a/idea/testData/android/lint/sharedPrefs.kt.173 b/idea/testData/android/lint/sharedPrefs.kt.173 new file mode 100644 index 00000000000..790c9fd9310 --- /dev/null +++ b/idea/testData/android/lint/sharedPrefs.kt.173 @@ -0,0 +1,127 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.preference.PreferenceManager + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class SharedPrefsText(context: Context) : Activity() { + // OK 1 + fun onCreate1(savedInstanceState: Bundle) { + super.onCreate(savedInstanceState) + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + val editor = preferences.edit() + editor.putString("foo", "bar") + editor.putInt("bar", 42) + editor.commit() + } + + // OK 2 + fun onCreate2(savedInstanceState: Bundle, apply: Boolean) { + super.onCreate(savedInstanceState) + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + val editor = preferences.edit() + editor.putString("foo", "bar") + editor.putInt("bar", 42) + if (apply) { + editor.apply() + } + } + + // OK using with lambda + fun withLambda() { + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + with(preferences.edit()) { + putString("foo", "bar") + putInt("bar", 42) + apply() + } + } + + // OK using apply lambda + fun testApplyLambda() { + PreferenceManager.getDefaultSharedPreferences(this).edit().apply { + putString("foo", "bar") + putInt("bar", 42) + apply() + } + } + + // OK using also lambda + fun testAlsoLambda() { + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + preferences.edit().also { + it.putString("foo", "bar") + it.putInt("bar", 42) + it.apply() + } + } + + // Not a bug + fun test(foo: Foo) { + val bar1 = foo.edit() + val bar3 = edit() + apply() + } + + internal fun apply() { + + } + + fun edit(): Bar { + return Bar() + } + + class Foo { + internal fun edit(): Bar { + return Bar() + } + } + + class Bar + + // Bug + fun bug1(savedInstanceState: Bundle) { + super.onCreate(savedInstanceState) + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + val editor = preferences.edit() + editor.putString("foo", "bar") + editor.putInt("bar", 42) + } + + // Bug missing commit in apply lambda + fun applyLambdaMissingCommit() { + PreferenceManager.getDefaultSharedPreferences(this).edit().apply { + putString("foo", "bar") + putInt("bar", 42) + } + } + + // Bug missing commit in also lambda + fun alsoLambdaMissingCommit() { + PreferenceManager.getDefaultSharedPreferences(this).edit().also { + it.putString("foo", "bar") + it.putInt("bar", 42) + } + } + + // Bug missing commit in with lambda + fun withLambdaMissingCommit() { + with(PreferenceManager.getDefaultSharedPreferences(this).edit()) { + putString("foo", "bar") + putInt("bar", 42) + } + } + + init { + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val editor = preferences.edit() + editor.putString("foo", "bar") + } + + fun testResultOfCommit() { + val r1 = PreferenceManager.getDefaultSharedPreferences(this).edit().putString("wat", "wat").commit() + val r2 = PreferenceManager.getDefaultSharedPreferences(this).edit().putString("wat", "wat").commit().toString() + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt b/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt index 5623005ea68..3932ff648a8 100644 --- a/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt +++ b/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt @@ -1,9 +1,9 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSQLiteStringInspection import android.database.sqlite.SQLiteDatabase fun test(db: SQLiteDatabase) { val a: String = 1 - db.execSQL("CREATE TABLE COMPANY(NAME STRING)") + db.execSQL("CREATE TABLE COMPANY(NAME STRING)") } \ No newline at end of file diff --git a/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt.173 b/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt.173 new file mode 100644 index 00000000000..5623005ea68 --- /dev/null +++ b/idea/testData/android/lint/showDiagnosticsWhenFileIsRed.kt.173 @@ -0,0 +1,9 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection + +import android.database.sqlite.SQLiteDatabase + +fun test(db: SQLiteDatabase) { + val a: String = 1 + + db.execSQL("CREATE TABLE COMPANY(NAME STRING)") +} \ No newline at end of file diff --git a/idea/testData/android/lint/sqlite.kt b/idea/testData/android/lint/sqlite.kt index 93eab539d4e..8c12de0e902 100644 --- a/idea/testData/android/lint/sqlite.kt +++ b/idea/testData/android/lint/sqlite.kt @@ -1,7 +1,7 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSQLiteStringInspection import android.database.sqlite.SQLiteDatabase fun test(db: SQLiteDatabase) { - db.execSQL("CREATE TABLE COMPANY(NAME STRING)") + db.execSQL("CREATE TABLE COMPANY(NAME STRING)") } \ No newline at end of file diff --git a/idea/testData/android/lint/sqlite.kt.173 b/idea/testData/android/lint/sqlite.kt.173 new file mode 100644 index 00000000000..93eab539d4e --- /dev/null +++ b/idea/testData/android/lint/sqlite.kt.173 @@ -0,0 +1,7 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection + +import android.database.sqlite.SQLiteDatabase + +fun test(db: SQLiteDatabase) { + db.execSQL("CREATE TABLE COMPANY(NAME STRING)") +} \ No newline at end of file diff --git a/idea/testData/android/lint/supportAnnotation.kt b/idea/testData/android/lint/supportAnnotation.kt index d6fae75c4c2..67eeeb3e98b 100644 --- a/idea/testData/android/lint/supportAnnotation.kt +++ b/idea/testData/android/lint/supportAnnotation.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSupportAnnotationUsageInspection // DEPENDENCY: IntRange.java -> android/support/annotation/IntRange.java // DEPENDENCY: RequiresPermission.java -> android/support/annotation/RequiresPermission.java diff --git a/idea/testData/android/lint/supportAnnotation.kt.173 b/idea/testData/android/lint/supportAnnotation.kt.173 new file mode 100644 index 00000000000..d6fae75c4c2 --- /dev/null +++ b/idea/testData/android/lint/supportAnnotation.kt.173 @@ -0,0 +1,36 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection +// DEPENDENCY: IntRange.java -> android/support/annotation/IntRange.java +// DEPENDENCY: RequiresPermission.java -> android/support/annotation/RequiresPermission.java + + +import android.support.annotation.IntRange +import android.support.annotation.RequiresPermission +import android.Manifest +import android.view.View + +const val constantVal = 0L + +@IntRange(from = 10, to = 0) +fun invalidRange1a(): Int = 5 + +@IntRange(from = constantVal, to = 10) // ok +fun invalidRange0b(): Int = 5 + +@IntRange(from = 10, to = constantVal) +fun invalidRange1b(): Int = 5 + + +// should be ok, KT-16600 +@RequiresPermission(anyOf = arrayOf(Manifest.permission.ACCESS_CHECKIN_PROPERTIES, + Manifest.permission.ACCESS_FINE_LOCATION)) +fun needsPermissions1() { } + +// should be ok, KT-16600 +@RequiresPermission(Manifest.permission.ACCESS_CHECKIN_PROPERTIES) +fun needsPermissions2() { } + +// error +@RequiresPermission( + value = Manifest.permission.ACCESS_CHECKIN_PROPERTIES, + anyOf = arrayOf(Manifest.permission.ACCESS_CHECKIN_PROPERTIES, Manifest.permission.ACCESS_FINE_LOCATION)) +fun needsPermissions3() { } \ No newline at end of file diff --git a/idea/testData/android/lint/systemServices.kt b/idea/testData/android/lint/systemServices.kt index 2e6860502b0..3433575a10f 100644 --- a/idea/testData/android/lint/systemServices.kt +++ b/idea/testData/android/lint/systemServices.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintServiceCastInspection import android.content.ClipboardManager import android.app.Activity @@ -13,8 +13,8 @@ class SystemServiceTest : Activity() { fun test1() { val displayServiceOk = getSystemService(DISPLAY_SERVICE) as DisplayManager val displayServiceWrong = getSystemService(DEVICE_POLICY_SERVICE) as DisplayManager - val wallPaperOk = getSystemService(WALLPAPER_SERVICE) as WallpaperService - val wallPaperWrong = getSystemService(WALLPAPER_SERVICE) as WallpaperManager + val wallPaperWrong = getSystemService(WALLPAPER_SERVICE) as WallpaperService + val wallPaperOk = getSystemService(WALLPAPER_SERVICE) as WallpaperManager } fun test2(context: Context) { diff --git a/idea/testData/android/lint/systemServices.kt.173 b/idea/testData/android/lint/systemServices.kt.173 new file mode 100644 index 00000000000..2e6860502b0 --- /dev/null +++ b/idea/testData/android/lint/systemServices.kt.173 @@ -0,0 +1,29 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection + +import android.content.ClipboardManager +import android.app.Activity +import android.app.WallpaperManager +import android.content.Context +import android.hardware.display.DisplayManager +import android.service.wallpaper.WallpaperService + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class SystemServiceTest : Activity() { + + fun test1() { + val displayServiceOk = getSystemService(DISPLAY_SERVICE) as DisplayManager + val displayServiceWrong = getSystemService(DEVICE_POLICY_SERVICE) as DisplayManager + val wallPaperOk = getSystemService(WALLPAPER_SERVICE) as WallpaperService + val wallPaperWrong = getSystemService(WALLPAPER_SERVICE) as WallpaperManager + } + + fun test2(context: Context) { + val displayServiceOk = context.getSystemService(DISPLAY_SERVICE) as DisplayManager + val displayServiceWrong = context.getSystemService(DEVICE_POLICY_SERVICE) as DisplayManager + } + + fun clipboard(context: Context) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard2 = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/toast.kt b/idea/testData/android/lint/toast.kt index 0623d189d20..06d9af08abe 100644 --- a/idea/testData/android/lint/toast.kt +++ b/idea/testData/android/lint/toast.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintShowToastInspection import android.app.Activity import android.content.Context @@ -24,7 +24,7 @@ class ToastTest(context: Context) : Activity() { } Runnable { - Toast.makeText(context, "foo", Toast.LENGTH_LONG) + Toast.makeText(context, "foo", Toast.LENGTH_LONG) } } @@ -45,13 +45,13 @@ class ToastTest(context: Context) : Activity() { private fun broken(context: Context) { // Errors - Toast.makeText(context, "foo", Toast.LENGTH_LONG) - val toast = Toast.makeText(context, R.string.app_name, 5000) + Toast.makeText(context, "foo", Toast.LENGTH_LONG) + val toast = Toast.makeText(context, R.string.app_name, 5000) toast.duration } init { - Toast.makeText(context, "foo", Toast.LENGTH_LONG) + Toast.makeText(context, "foo", Toast.LENGTH_LONG) } @android.annotation.SuppressLint("ShowToast") diff --git a/idea/testData/android/lint/toast.kt.173 b/idea/testData/android/lint/toast.kt.173 new file mode 100644 index 00000000000..0623d189d20 --- /dev/null +++ b/idea/testData/android/lint/toast.kt.173 @@ -0,0 +1,75 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection + +import android.app.Activity +import android.content.Context +import android.widget.Toast + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class ToastTest(context: Context) : Activity() { + private fun createToast(context: Context): Toast { + // Don't warn here + return Toast.makeText(context, "foo", Toast.LENGTH_LONG) + } + + private fun insideRunnable(context: Context) { + Runnable { + Toast.makeText(context, "foo", Toast.LENGTH_LONG).show() + } + + Runnable { + val toast = Toast.makeText(context, "foo", Toast.LENGTH_LONG) + if (5 > 3) { + toast.show() + } + } + + Runnable { + Toast.makeText(context, "foo", Toast.LENGTH_LONG) + } + } + + private fun showToast(context: Context) { + // Don't warn here + val toast = Toast.makeText(context, "foo", Toast.LENGTH_LONG) + System.out.println("Other intermediate code here") + val temp = 5 + 2 + toast.show() + } + + private fun showToast2(context: Context) { + // Don't warn here + val duration = Toast.LENGTH_LONG + Toast.makeText(context, "foo", Toast.LENGTH_LONG).show() + Toast.makeText(context, R.string.app_name, duration).show() + } + + private fun broken(context: Context) { + // Errors + Toast.makeText(context, "foo", Toast.LENGTH_LONG) + val toast = Toast.makeText(context, R.string.app_name, 5000) + toast.duration + } + + init { + Toast.makeText(context, "foo", Toast.LENGTH_LONG) + } + + @android.annotation.SuppressLint("ShowToast") + private fun checkSuppress1(context: Context) { + val toast = Toast.makeText(this, "MyToast", Toast.LENGTH_LONG) + } + + private fun checkSuppress2(context: Context) { + @android.annotation.SuppressLint("ShowToast") + val toast1 = Toast.makeText(this, "MyToast", Toast.LENGTH_LONG) + + @android.annotation.SuppressLint("ShowToast", "Lorem ipsum") + val toast2 = Toast.makeText(this, "MyToast", Toast.LENGTH_LONG) + } + + class R { + object string { + val app_name = 1 + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/valueOf.kt b/idea/testData/android/lint/valueOf.kt index 1702302715f..83565a66746 100644 --- a/idea/testData/android/lint/valueOf.kt +++ b/idea/testData/android/lint/valueOf.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintUseValueOfInspection @Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") class Simple { diff --git a/idea/testData/android/lint/valueOf.kt.173 b/idea/testData/android/lint/valueOf.kt.173 new file mode 100644 index 00000000000..1702302715f --- /dev/null +++ b/idea/testData/android/lint/valueOf.kt.173 @@ -0,0 +1,8 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class Simple { + fun test() { + Integer(5) + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/velocityTrackerRecycle.kt b/idea/testData/android/lint/velocityTrackerRecycle.kt index 73a5933636d..9d258a5130b 100644 --- a/idea/testData/android/lint/velocityTrackerRecycle.kt +++ b/idea/testData/android/lint/velocityTrackerRecycle.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintRecycleInspection @file:Suppress("UNUSED_VARIABLE") @@ -11,7 +11,7 @@ class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - VelocityTracker.obtain() + VelocityTracker.obtain() VelocityTracker.obtain().recycle() diff --git a/idea/testData/android/lint/velocityTrackerRecycle.kt.173 b/idea/testData/android/lint/velocityTrackerRecycle.kt.173 new file mode 100644 index 00000000000..73a5933636d --- /dev/null +++ b/idea/testData/android/lint/velocityTrackerRecycle.kt.173 @@ -0,0 +1,23 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection + +@file:Suppress("UNUSED_VARIABLE") + +import android.app.Activity +import android.os.Bundle +import android.view.VelocityTracker + +class MainActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + VelocityTracker.obtain() + + VelocityTracker.obtain().recycle() + + val v1 = VelocityTracker.obtain() + + val v2 = VelocityTracker.obtain() + v2.recycle() + } +} diff --git a/idea/testData/android/lint/viewConstructor.kt b/idea/testData/android/lint/viewConstructor.kt index 2087cee75c4..9b1e29e7fff 100644 --- a/idea/testData/android/lint/viewConstructor.kt +++ b/idea/testData/android/lint/viewConstructor.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintViewConstructorInspection import android.content.Context import android.util.AttributeSet diff --git a/idea/testData/android/lint/viewConstructor.kt.173 b/idea/testData/android/lint/viewConstructor.kt.173 new file mode 100644 index 00000000000..2087cee75c4 --- /dev/null +++ b/idea/testData/android/lint/viewConstructor.kt.173 @@ -0,0 +1,32 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.TextView + +class View1(context: Context?) : View(context) +class View2(context: Context?, attrs: AttributeSet?) : View(context, attrs) +class View3(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : TextView(context, attrs, defStyleAttr) + +// Error +class View4(int: Int, context: Context?) : View(context) + +// Error +class View5(context: Context?, attrs: AttributeSet?, val name: String) : View(context, attrs) + +class View6 : View { + constructor(context: Context) : super(context) { + + } +} + +class View7 : View { + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) +} + +// Error +class View8 : View { + constructor(context: Context, a: Int) : super(context) +} \ No newline at end of file diff --git a/idea/testData/android/lint/viewHolder.kt b/idea/testData/android/lint/viewHolder.kt index 057b2d07aa8..551027b35f8 100644 --- a/idea/testData/android/lint/viewHolder.kt +++ b/idea/testData/android/lint/viewHolder.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintViewHolderInspection @file:Suppress("NAME_SHADOWING", "unused", "UNUSED_VALUE", "VARIABLE_WITH_REDUNDANT_INITIALIZER", "UNUSED_VARIABLE") @@ -27,7 +27,7 @@ abstract class ViewHolderTest : BaseAdapter() { override fun getView(position: Int, convertView: View, parent: ViewGroup): View { var convertView = convertView // Should use View Holder pattern here - convertView = mInflater.inflate(R.layout.your_layout, null) + convertView = mInflater.inflate(R.layout.your_layout, null) val text: TextView = convertView.findViewById(R.id.text) text.text = "Position " + position diff --git a/idea/testData/android/lint/viewHolder.kt.173 b/idea/testData/android/lint/viewHolder.kt.173 new file mode 100644 index 00000000000..057b2d07aa8 --- /dev/null +++ b/idea/testData/android/lint/viewHolder.kt.173 @@ -0,0 +1,154 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection + +@file:Suppress("NAME_SHADOWING", "unused", "UNUSED_VALUE", "VARIABLE_WITH_REDUNDANT_INITIALIZER", "UNUSED_VARIABLE") + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.LinearLayout +import android.widget.TextView +import java.util.ArrayList + +@SuppressWarnings("ConstantConditions", "UnusedDeclaration") +abstract class ViewHolderTest : BaseAdapter() { + override fun getCount() = 0 + override fun getItem(position: Int) = null + override fun getItemId(position: Int) = 0L + + class Adapter1 : ViewHolderTest() { + override fun getView(position: Int, convertView: View, parent: ViewGroup) = null + } + + class Adapter2 : ViewHolderTest() { + lateinit var mInflater: LayoutInflater + + override fun getView(position: Int, convertView: View, parent: ViewGroup): View { + var convertView = convertView + // Should use View Holder pattern here + convertView = mInflater.inflate(R.layout.your_layout, null) + + val text: TextView = convertView.findViewById(R.id.text) + text.text = "Position " + position + + return convertView + } + } + + class Adapter3 : ViewHolderTest() { + lateinit var mInflater: LayoutInflater + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + // Already using View Holder pattern + if (convertView == null) { + convertView = mInflater.inflate(R.layout.your_layout, null) + } + + val text: TextView = convertView!!.findViewById(R.id.text) + text.text = "Position " + position + + return convertView + } + } + + class Adapter4 : ViewHolderTest() { + lateinit var mInflater: LayoutInflater + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + // Already using View Holder pattern + //noinspection StatementWithEmptyBody + if (convertView != null) { + } else { + convertView = mInflater.inflate(R.layout.your_layout, null) + } + + val text: TextView = convertView!!.findViewById(R.id.text) + text.text = "Position " + position + + return convertView + } + } + + class Adapter5 : ViewHolderTest() { + lateinit var mInflater: LayoutInflater + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + // Already using View Holder pattern + convertView = if (convertView == null) mInflater.inflate(R.layout.your_layout, null) else convertView + + val text: TextView = convertView!!.findViewById(R.id.text) + text.text = "Position " + position + + return convertView + } + } + + class Adapter6 : ViewHolderTest() { + private val mContext: Context? = null + private var mLayoutInflator: LayoutInflater? = null + private lateinit var mLapTimes: ArrayList + + override fun getView(position: Int, convertView: View, parent: ViewGroup): View { + if (mLayoutInflator == null) + mLayoutInflator = mContext!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + + var v: View? = convertView + if (v == null) v = mLayoutInflator!!.inflate(R.layout.your_layout, null) + + val listItemHolder: LinearLayout = v!!.findViewById(R.id.laptimes_list_item_holder) + listItemHolder.removeAllViews() + + for (i in 1..5) { + val lapItemView: View = mLayoutInflator!!.inflate(R.layout.laptime_item, null) + if (i == 0) { + val t: TextView = lapItemView.findViewById(R.id.laptime_text) + } + + val t2: TextView = lapItemView.findViewById(R.id.laptime_text2) + if (i < mLapTimes.size - 1 && mLapTimes.size > 1) { + var laptime = mLapTimes[i] - mLapTimes[i + 1] + if (laptime < 0) laptime = mLapTimes[i] + } + + listItemHolder.addView(lapItemView) + + } + return v + } + } + + class Adapter7 : ViewHolderTest() { + lateinit var inflater: LayoutInflater + + override fun getView(position: Int, convertView: View, parent: ViewGroup): View { + var rootView: View? = convertView + val itemViewType = getItemViewType(position) + when (itemViewType) { + 0 -> { + if (rootView != null) + return rootView + rootView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false) + } + } + return rootView!! + } + } + + class R { + object layout { + val your_layout = 1 + val laptime_item = 2 + } + + object id { + val laptime_text = 1 + val laptime_text2 = 2 + val laptimes_list_item_holder = 3 + val text = 4 + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lint/wrongAnnotation.kt b/idea/testData/android/lint/wrongAnnotation.kt index 163256a6b51..93f95fe555c 100644 --- a/idea/testData/android/lint/wrongAnnotation.kt +++ b/idea/testData/android/lint/wrongAnnotation.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintLocalSuppressInspection import android.annotation.SuppressLint import android.view.View @@ -13,15 +13,15 @@ class WrongAnnotation2 { companion object { @SuppressLint("NewApi") // Valid: class-file check on method - fun foobar(view: View, @SuppressLint("NewApi") foo: Int) { + fun foobar(view: View, @SuppressLint("NewApi") foo: Int) { // Invalid: class-file check @SuppressLint("NewApi") // Invalid val a: Boolean - @SuppressLint("SdCardPath", "NewApi") // TODO: Invalid, class-file based check on local variable + @SuppressLint("SdCardPath", "NewApi") // TODO: Invalid, class-file based check on local variable val b: Boolean - @android.annotation.SuppressLint("SdCardPath", "NewApi") // TDOD: Invalid (FQN) + @android.annotation.SuppressLint("SdCardPath", "NewApi") // TDOD: Invalid (FQN) val c: Boolean @SuppressLint("SdCardPath") // Valid: AST-based check diff --git a/idea/testData/android/lint/wrongAnnotation.kt.173 b/idea/testData/android/lint/wrongAnnotation.kt.173 new file mode 100644 index 00000000000..163256a6b51 --- /dev/null +++ b/idea/testData/android/lint/wrongAnnotation.kt.173 @@ -0,0 +1,42 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection + +import android.annotation.SuppressLint +import android.view.View + +@Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION") +class WrongAnnotation2 { + @SuppressLint("NewApi") + private val field1: Int = 0 + + @SuppressLint("NewApi") + private val field2 = 5 + + companion object { + @SuppressLint("NewApi") // Valid: class-file check on method + fun foobar(view: View, @SuppressLint("NewApi") foo: Int) { + // Invalid: class-file check + @SuppressLint("NewApi") // Invalid + val a: Boolean + + @SuppressLint("SdCardPath", "NewApi") // TODO: Invalid, class-file based check on local variable + val b: Boolean + + @android.annotation.SuppressLint("SdCardPath", "NewApi") // TDOD: Invalid (FQN) + val c: Boolean + + @SuppressLint("SdCardPath") // Valid: AST-based check + val d: Boolean + } + + init { + // Local variable outside method: invalid + @SuppressLint("NewApi") + val localvar = 5 + } + + private fun test() { + @SuppressLint("NewApi") // Invalid + val a = View.MEASURED_STATE_MASK + } + } +} diff --git a/idea/testData/android/lint/wrongImport.kt b/idea/testData/android/lint/wrongImport.kt index b688362b490..1c45d892953 100644 --- a/idea/testData/android/lint/wrongImport.kt +++ b/idea/testData/android/lint/wrongImport.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSuspiciousImportInspection //Warning import android.R diff --git a/idea/testData/android/lint/wrongImport.kt.173 b/idea/testData/android/lint/wrongImport.kt.173 new file mode 100644 index 00000000000..b688362b490 --- /dev/null +++ b/idea/testData/android/lint/wrongImport.kt.173 @@ -0,0 +1,8 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection + +//Warning +import android.R + +fun a() { + R.id.button1 +} \ No newline at end of file diff --git a/idea/testData/android/lint/wrongViewCall.kt b/idea/testData/android/lint/wrongViewCall.kt index 0ee6e922d91..ce7b5063d2f 100644 --- a/idea/testData/android/lint/wrongViewCall.kt +++ b/idea/testData/android/lint/wrongViewCall.kt @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintWrongCallInspection import android.content.Context import android.graphics.Canvas diff --git a/idea/testData/android/lint/wrongViewCall.kt.173 b/idea/testData/android/lint/wrongViewCall.kt.173 new file mode 100644 index 00000000000..0ee6e922d91 --- /dev/null +++ b/idea/testData/android/lint/wrongViewCall.kt.173 @@ -0,0 +1,23 @@ +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection + +import android.content.Context +import android.graphics.Canvas +import android.util.AttributeSet +import android.widget.FrameLayout +import android.widget.LinearLayout + +abstract class WrongViewCall(context: Context, attrs: AttributeSet, defStyle: Int) : LinearLayout(context, attrs, defStyle) { + private val child: MyChild? = null + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + child?.onDraw(canvas) + } + + private inner class MyChild(context: Context, attrs: AttributeSet, defStyle: Int) : FrameLayout(context, attrs, defStyle) { + + public override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + } + } +} diff --git a/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt index 6274bc4d340..cdfb473d8a6 100644 --- a/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt +++ b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add Parcelable Implementation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection import android.os.Parcel import android.os.Parcelable diff --git a/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.173 b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.173 new file mode 100644 index 00000000000..6274bc4d340 --- /dev/null +++ b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.173 @@ -0,0 +1,14 @@ +// INTENTION_TEXT: Add Parcelable Implementation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +import android.os.Parcel +import android.os.Parcelable + +class MissingCreator : Parcelable { + override fun writeToParcel(dest: Parcel?, flags: Int) { + TODO("not implemented") + } + + override fun describeContents(): Int { + TODO("not implemented") + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected index c4ccf101b42..29ea448d7ae 100644 --- a/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected +++ b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add Parcelable Implementation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection import android.os.Parcel import android.os.Parcelable diff --git a/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected.173 b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected.173 new file mode 100644 index 00000000000..c4ccf101b42 --- /dev/null +++ b/idea/testData/android/lintQuickfix/parcelable/missingCreator.kt.expected.173 @@ -0,0 +1,27 @@ +// INTENTION_TEXT: Add Parcelable Implementation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +import android.os.Parcel +import android.os.Parcelable + +class MissingCreator() : Parcelable { + constructor(parcel: Parcel) : this() { + } + + override fun writeToParcel(dest: Parcel?, flags: Int) { + TODO("not implemented") + } + + override fun describeContents(): Int { + TODO("not implemented") + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): MissingCreator { + return MissingCreator(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt index e045a311575..09eafd5b62e 100644 --- a/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt +++ b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add Parcelable Implementation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection import android.os.Parcelable class NoImplementation : Parcelable \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.173 b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.173 new file mode 100644 index 00000000000..e045a311575 --- /dev/null +++ b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.173 @@ -0,0 +1,5 @@ +// INTENTION_TEXT: Add Parcelable Implementation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +import android.os.Parcelable + +class NoImplementation : Parcelable \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected index 6ebc78fbe7e..f364327f3d2 100644 --- a/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected +++ b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add Parcelable Implementation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintParcelCreatorInspection import android.os.Parcel import android.os.Parcelable diff --git a/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected.173 b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected.173 new file mode 100644 index 00000000000..6ebc78fbe7e --- /dev/null +++ b/idea/testData/android/lintQuickfix/parcelable/noImplementation.kt.expected.173 @@ -0,0 +1,27 @@ +// INTENTION_TEXT: Add Parcelable Implementation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection +import android.os.Parcel +import android.os.Parcelable + +class NoImplementation() : Parcelable { + constructor(parcel: Parcel) : this() { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): NoImplementation { + return NoImplementation(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/annotation.kt b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt index dbf09a01c09..19b6ba7d86a 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/annotation.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.173 new file mode 100644 index 00000000000..dbf09a01c09 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import kotlin.reflect.KClass + +annotation class SomeAnnotationWithClass(val cls: KClass<*>) + +@SomeAnnotationWithClass(VectorDrawable::class) +class VectorDrawableProvider { +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected index 2db4b45bc3f..dbc848be75d 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected.173 new file mode 100644 index 00000000000..2db4b45bc3f --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/annotation.kt.expected.173 @@ -0,0 +1,15 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi +import kotlin.reflect.KClass + +annotation class SomeAnnotationWithClass(val cls: KClass<*>) + +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +@SomeAnnotationWithClass(VectorDrawable::class) +class VectorDrawableProvider { +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/companion.kt b/idea/testData/android/lintQuickfix/requiresApi/companion.kt index d3325230e28..be0bb206f63 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/companion.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/companion.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/companion.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/companion.kt.173 new file mode 100644 index 00000000000..d3325230e28 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/companion.kt.173 @@ -0,0 +1,11 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + companion object { + val VECTOR_DRAWABLE = VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected index 1858d14b757..d2fc869db12 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected.173 new file mode 100644 index 00000000000..1858d14b757 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/companion.kt.expected.173 @@ -0,0 +1,14 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + +class VectorDrawableProvider { + companion object { + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + val VECTOR_DRAWABLE = VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt index 9f7d6c647aa..c8a4583498e 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.173 new file mode 100644 index 00000000000..9f7d6c647aa --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + + +fun withDefaultParameter(vector: VectorDrawable = VectorDrawable()) { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected index 31602b7a3ab..8d3fcc4c207 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected.173 new file mode 100644 index 00000000000..31602b7a3ab --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/defaultParameter.kt.expected.173 @@ -0,0 +1,13 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + + +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +fun withDefaultParameter(vector: VectorDrawable = VectorDrawable()) { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/extend.kt b/idea/testData/android/lintQuickfix/requiresApi/extend.kt index 7b955856b4b..6e53535aa1d 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/extend.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/extend.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/extend.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/extend.kt.173 new file mode 100644 index 00000000000..7b955856b4b --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/extend.kt.173 @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + +class MyVectorDrawable : VectorDrawable() { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected index 1965006bd62..3307f07e34e 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected.173 new file mode 100644 index 00000000000..1965006bd62 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/extend.kt.expected.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +class MyVectorDrawable : VectorDrawable() { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt index 9b04c508ebe..c6016fd4012 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.173 new file mode 100644 index 00000000000..9b04c508ebe --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.173 @@ -0,0 +1,13 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + with(this) { + return VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected index fad5bf9a068..ff231d64d5a 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected.173 new file mode 100644 index 00000000000..fad5bf9a068 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/functionLiteral.kt.expected.173 @@ -0,0 +1,16 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + +class VectorDrawableProvider { + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + fun getVectorDrawable(): VectorDrawable { + with(this) { + return VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt index ee42d761ab0..025b3d3d130 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(KITKAT) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java class Test { diff --git a/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.173 new file mode 100644 index 00000000000..ee42d761ab0 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.173 @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Add @RequiresApi(KITKAT) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +class Test { + fun foo(): Int { + return android.R.attr.windowTranslucentStatus + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected index c94e41a56aa..8151e13fa3d 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected @@ -2,7 +2,7 @@ import android.os.Build import android.support.annotation.RequiresApi // INTENTION_TEXT: Add @RequiresApi(KITKAT) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java class Test { diff --git a/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected.173 new file mode 100644 index 00000000000..c94e41a56aa --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/inlinedConstant.kt.expected.173 @@ -0,0 +1,13 @@ +import android.os.Build +import android.support.annotation.RequiresApi + +// INTENTION_TEXT: Add @RequiresApi(KITKAT) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +class Test { + @RequiresApi(Build.VERSION_CODES.KITKAT) + fun foo(): Int { + return android.R.attr.windowTranslucentStatus + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/method.kt b/idea/testData/android/lintQuickfix/requiresApi/method.kt index 79068fceb87..efbd6dcc173 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/method.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/method.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/method.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/method.kt.173 new file mode 100644 index 00000000000..79068fceb87 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/method.kt.173 @@ -0,0 +1,11 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + return VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected index 97c45ca4729..3183461bf58 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected.173 new file mode 100644 index 00000000000..97c45ca4729 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/method.kt.expected.173 @@ -0,0 +1,14 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + +class VectorDrawableProvider { + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + fun getVectorDrawable(): VectorDrawable { + return VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/property.kt b/idea/testData/android/lintQuickfix/requiresApi/property.kt index d4c0ae8be3a..d73223f8c0d 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/property.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/property.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/property.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/property.kt.173 new file mode 100644 index 00000000000..d4c0ae8be3a --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/property.kt.173 @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val VECTOR_DRAWABLE = VectorDrawable() +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected index 09a6dbc8157..2da5890a952 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected.173 new file mode 100644 index 00000000000..09a6dbc8157 --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/property.kt.expected.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + +class VectorDrawableProvider { + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + val VECTOR_DRAWABLE = VectorDrawable() +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt index d7f7f1f349a..7e7564b542c 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(M) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.app.Activity diff --git a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.173 new file mode 100644 index 00000000000..d7f7f1f349a --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.173 @@ -0,0 +1,7 @@ +// INTENTION_TEXT: Add @RequiresApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java +import android.app.Activity + +val top: Int + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected index 3460d7b564f..01d63d7c860 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(M) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.app.Activity import android.os.Build diff --git a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected.173 new file mode 100644 index 00000000000..3460d7b564f --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Add @RequiresApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java +import android.app.Activity +import android.os.Build +import android.support.annotation.RequiresApi + +val top: Int + @RequiresApi(Build.VERSION_CODES.M) + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/when.kt b/idea/testData/android/lintQuickfix/requiresApi/when.kt index 67d45b87a8a..0ff7c0cc893 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/when.kt +++ b/idea/testData/android/lintQuickfix/requiresApi/when.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/when.kt.173 b/idea/testData/android/lintQuickfix/requiresApi/when.kt.173 new file mode 100644 index 00000000000..67d45b87a8a --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/when.kt.173 @@ -0,0 +1,15 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + return when (flag) { + true -> VectorDrawable() + else -> VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected index 3126a6e75be..ce78fc0c8fe 100644 --- a/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected +++ b/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected.173 b/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected.173 new file mode 100644 index 00000000000..3126a6e75be --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/when.kt.expected.173 @@ -0,0 +1,18 @@ +// INTENTION_TEXT: Add @RequiresApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java + +import android.graphics.drawable.VectorDrawable +import android.os.Build +import android.support.annotation.RequiresApi + +class VectorDrawableProvider { + val flag = false + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + fun getVectorDrawable(): VectorDrawable { + return when (flag) { + true -> VectorDrawable() + else -> VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt index 34cac7d0d3a..02d9669b194 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection import android.app.Activity import android.os.Environment diff --git a/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.173 new file mode 100644 index 00000000000..34cac7d0d3a --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +import android.app.Activity +import android.os.Environment + + +class MainActivity : Activity() { + fun getSdCard(fromEnvironment: Boolean) = if (fromEnvironment) Environment.getExternalStorageDirectory().path else "/sdcard" +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected index 846afb93f20..e95b0a1980b 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection import android.annotation.SuppressLint import android.app.Activity diff --git a/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected.173 new file mode 100644 index 00000000000..846afb93f20 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/activityMethod.kt.expected.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +import android.annotation.SuppressLint +import android.app.Activity +import android.os.Environment + + +class MainActivity : Activity() { + @SuppressLint("SdCardPath") + fun getSdCard(fromEnvironment: Boolean) = if (fromEnvironment) Environment.getExternalStorageDirectory().path else "/sdcard" +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt index a0a917d5854..26adf3d65c7 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection import android.annotation.SuppressLint import android.app.Activity diff --git a/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.173 new file mode 100644 index 00000000000..a0a917d5854 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +import android.annotation.SuppressLint +import android.app.Activity +import android.os.Environment + + +class MainActivity : Activity() { + @SuppressLint("Something") + fun getSdCard(fromEnvironment: Boolean) = if (fromEnvironment) Environment.getExternalStorageDirectory().path else "/sdcard" +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected index c434eea2f1e..45d7bb0130b 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection import android.annotation.SuppressLint import android.app.Activity diff --git a/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected.173 new file mode 100644 index 00000000000..c434eea2f1e --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/addToExistingAnnotation.kt.expected.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +import android.annotation.SuppressLint +import android.app.Activity +import android.os.Environment + + +class MainActivity : Activity() { + @SuppressLint("Something", "SdCardPath") + fun getSdCard(fromEnvironment: Boolean) = if (fromEnvironment) Environment.getExternalStorageDirectory().path else "/sdcard" +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt index bfed3b9be56..1d325d1db70 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt @@ -1,4 +1,4 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection class SdCard(val path: String = "/sdcard") \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.173 new file mode 100644 index 00000000000..bfed3b9be56 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.173 @@ -0,0 +1,4 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +class SdCard(val path: String = "/sdcard") \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected index 68149e8eef5..8b730ed0777 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected @@ -1,6 +1,6 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection class SdCard(@SuppressLint("SdCardPath") val path: String = "/sdcard") \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected.173 new file mode 100644 index 00000000000..68149e8eef5 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/constructorParameter.kt.expected.173 @@ -0,0 +1,6 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +class SdCard(@SuppressLint("SdCardPath") val path: String = "/sdcard") \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt index 346336db2f8..6d8e858b66b 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo() { val (a: String, b: String) = "/sdcard" diff --git a/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.173 new file mode 100644 index 00000000000..346336db2f8 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.173 @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo() { + val (a: String, b: String) = "/sdcard" +} + +operator fun CharSequence.component1(): String = "component1" +operator fun CharSequence.component2(): String = "component2" diff --git a/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected index 52c9012bfb0..7022cdfb459 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected @@ -1,7 +1,7 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection @SuppressLint("SdCardPath") fun foo() { diff --git a/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected.173 new file mode 100644 index 00000000000..52c9012bfb0 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/destructuringDeclaration.kt.expected.173 @@ -0,0 +1,12 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +@SuppressLint("SdCardPath") +fun foo() { + val (a: String, b: String) = "/sdcard" +} + +operator fun CharSequence.component1(): String = "component1" +operator fun CharSequence.component2(): String = "component2" diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt index 08556f1ec6c..9da3c3d1779 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo(l: Any) = l diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.173 new file mode 100644 index 00000000000..08556f1ec6c --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo(l: Any) = l + +fun bar() { + foo() { + "/sdcard" + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected index 34e862ad244..db344edeea7 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected @@ -1,7 +1,7 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo(l: Any) = l diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected.173 new file mode 100644 index 00000000000..34e862ad244 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgument.kt.expected.173 @@ -0,0 +1,13 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo(l: Any) = l + +@SuppressLint("SdCardPath") +fun bar() { + foo() { + "/sdcard" + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt index 233767c23ce..25de06b3bb6 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo(l: Any) = l diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.173 new file mode 100644 index 00000000000..233767c23ce --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.173 @@ -0,0 +1,6 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo(l: Any) = l + +val bar = foo() { "/sdcard" } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected index 575e18d9392..617e6b33731 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected @@ -1,7 +1,7 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo(l: Any) = l diff --git a/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected.173 new file mode 100644 index 00000000000..575e18d9392 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/lambdaArgumentProperty.kt.expected.173 @@ -0,0 +1,9 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo(l: Any) = l + +@SuppressLint("SdCardPath") +val bar = foo() { "/sdcard" } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt index 854638e52c2..9abdb51fd35 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt @@ -1,4 +1,4 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo(path: String = "/sdcard") = path \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.173 new file mode 100644 index 00000000000..854638e52c2 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.173 @@ -0,0 +1,4 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo(path: String = "/sdcard") = path \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected index 481ddf3fff0..99c23d0552e 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected @@ -1,6 +1,6 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection fun foo(@SuppressLint("SdCardPath") path: String = "/sdcard") = path \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected.173 new file mode 100644 index 00000000000..481ddf3fff0 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/methodParameter.kt.expected.173 @@ -0,0 +1,6 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +fun foo(@SuppressLint("SdCardPath") path: String = "/sdcard") = path \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt index 8afada5c2d6..b8f5cdf95e0 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt @@ -1,4 +1,4 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection val getPath = { "/sdcard" } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.173 new file mode 100644 index 00000000000..8afada5c2d6 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.173 @@ -0,0 +1,4 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +val getPath = { "/sdcard" } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected index 65e05d26d97..8f4ef7d5ea3 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected @@ -1,7 +1,7 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection @SuppressLint("SdCardPath") val getPath = { "/sdcard" } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected.173 new file mode 100644 index 00000000000..65e05d26d97 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/propertyWithLambda.kt.expected.173 @@ -0,0 +1,7 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +@SuppressLint("SdCardPath") +val getPath = { "/sdcard" } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt index c4fee12198d..335913d069c 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt +++ b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt @@ -1,4 +1,4 @@ // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection val path = "/sdcard" \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.173 b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.173 new file mode 100644 index 00000000000..c4fee12198d --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.173 @@ -0,0 +1,4 @@ +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +val path = "/sdcard" \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected index 1b00a0a7951..9fc27e6b7dc 100644 --- a/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected +++ b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected @@ -1,7 +1,7 @@ import android.annotation.SuppressLint // INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintSdCardPathInspection @SuppressLint("SdCardPath") val path = "/sdcard" \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected.173 b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected.173 new file mode 100644 index 00000000000..1b00a0a7951 --- /dev/null +++ b/idea/testData/android/lintQuickfix/suppressLint/simpleProperty.kt.expected.173 @@ -0,0 +1,7 @@ +import android.annotation.SuppressLint + +// INTENTION_TEXT: Suppress: Add @SuppressLint("SdCardPath") annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection + +@SuppressLint("SdCardPath") +val path = "/sdcard" \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/annotation.kt b/idea/testData/android/lintQuickfix/targetApi/annotation.kt index f9f055359d7..e9431202f81 100644 --- a/idea/testData/android/lintQuickfix/targetApi/annotation.kt +++ b/idea/testData/android/lintQuickfix/targetApi/annotation.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import kotlin.reflect.KClass diff --git a/idea/testData/android/lintQuickfix/targetApi/annotation.kt.173 b/idea/testData/android/lintQuickfix/targetApi/annotation.kt.173 new file mode 100644 index 00000000000..f9f055359d7 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/annotation.kt.173 @@ -0,0 +1,11 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import kotlin.reflect.KClass + +annotation class SomeAnnotationWithClass(val cls: KClass<*>) + +@SomeAnnotationWithClass(VectorDrawable::class) +class VectorDrawableProvider { +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected b/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected index 738d2a571d1..0a51e121500 100644 --- a/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected.173 new file mode 100644 index 00000000000..738d2a571d1 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/annotation.kt.expected.173 @@ -0,0 +1,14 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build +import kotlin.reflect.KClass + +annotation class SomeAnnotationWithClass(val cls: KClass<*>) + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +@SomeAnnotationWithClass(VectorDrawable::class) +class VectorDrawableProvider { +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/companion.kt b/idea/testData/android/lintQuickfix/targetApi/companion.kt index a35ae52f4bc..d0dd43774c8 100644 --- a/idea/testData/android/lintQuickfix/targetApi/companion.kt +++ b/idea/testData/android/lintQuickfix/targetApi/companion.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/companion.kt.173 b/idea/testData/android/lintQuickfix/targetApi/companion.kt.173 new file mode 100644 index 00000000000..a35ae52f4bc --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/companion.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + companion object { + val VECTOR_DRAWABLE = VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected b/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected index 570322550cb..ae413f19a2a 100644 --- a/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected.173 new file mode 100644 index 00000000000..570322550cb --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/companion.kt.expected.173 @@ -0,0 +1,13 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + companion object { + val VECTOR_DRAWABLE = VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt index 8cff109b6cd..441ac662838 100644 --- a/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt +++ b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.173 b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.173 new file mode 100644 index 00000000000..8cff109b6cd --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.173 @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + + +fun withDefaultParameter(vector: VectorDrawable = VectorDrawable()) { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected index 1b00e2bc819..4a7ffec0d23 100644 --- a/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected.173 new file mode 100644 index 00000000000..1b00e2bc819 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/defaultParameter.kt.expected.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +fun withDefaultParameter(vector: VectorDrawable = VectorDrawable()) { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/extend.kt b/idea/testData/android/lintQuickfix/targetApi/extend.kt index b47ea573746..8e49ae1bd73 100644 --- a/idea/testData/android/lintQuickfix/targetApi/extend.kt +++ b/idea/testData/android/lintQuickfix/targetApi/extend.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/extend.kt.173 b/idea/testData/android/lintQuickfix/targetApi/extend.kt.173 new file mode 100644 index 00000000000..b47ea573746 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/extend.kt.173 @@ -0,0 +1,8 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class MyVectorDrawable : VectorDrawable() { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected b/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected index 6ea867dfef8..7a1cd362baa 100644 --- a/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected.173 new file mode 100644 index 00000000000..6ea867dfef8 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/extend.kt.expected.173 @@ -0,0 +1,11 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +class MyVectorDrawable : VectorDrawable() { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt index ea1b9c2c64b..321913b70a9 100644 --- a/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt +++ b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.173 b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.173 new file mode 100644 index 00000000000..ea1b9c2c64b --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + with(this) { + return VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected index 3c31dd270f4..b99ca947fb6 100644 --- a/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected.173 new file mode 100644 index 00000000000..3c31dd270f4 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/functionLiteral.kt.expected.173 @@ -0,0 +1,15 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + fun getVectorDrawable(): VectorDrawable { + with(this) { + return VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt index ed8cd3c35d9..8b233964e07 100644 --- a/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt +++ b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(KITKAT) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection class Test { fun foo(): Int { diff --git a/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.173 b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.173 new file mode 100644 index 00000000000..ed8cd3c35d9 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.173 @@ -0,0 +1,8 @@ +// INTENTION_TEXT: Add @TargetApi(KITKAT) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection + +class Test { + fun foo(): Int { + return android.R.attr.windowTranslucentStatus + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected index 5e788aca384..84d86bc3776 100644 --- a/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected @@ -2,7 +2,7 @@ import android.annotation.TargetApi import android.os.Build // INTENTION_TEXT: Add @TargetApi(KITKAT) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection class Test { @TargetApi(Build.VERSION_CODES.KITKAT) diff --git a/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected.173 new file mode 100644 index 00000000000..5e788aca384 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/inlinedConstant.kt.expected.173 @@ -0,0 +1,12 @@ +import android.annotation.TargetApi +import android.os.Build + +// INTENTION_TEXT: Add @TargetApi(KITKAT) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection + +class Test { + @TargetApi(Build.VERSION_CODES.KITKAT) + fun foo(): Int { + return android.R.attr.windowTranslucentStatus + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/method.kt b/idea/testData/android/lintQuickfix/targetApi/method.kt index 4df86f64ee4..89acdda7b32 100644 --- a/idea/testData/android/lintQuickfix/targetApi/method.kt +++ b/idea/testData/android/lintQuickfix/targetApi/method.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/method.kt.173 b/idea/testData/android/lintQuickfix/targetApi/method.kt.173 new file mode 100644 index 00000000000..4df86f64ee4 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/method.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + return VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/method.kt.expected b/idea/testData/android/lintQuickfix/targetApi/method.kt.expected index 8e27e69701f..7f10d4b9893 100644 --- a/idea/testData/android/lintQuickfix/targetApi/method.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/method.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/method.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/method.kt.expected.173 new file mode 100644 index 00000000000..8e27e69701f --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/method.kt.expected.173 @@ -0,0 +1,13 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + fun getVectorDrawable(): VectorDrawable { + return VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/property.kt b/idea/testData/android/lintQuickfix/targetApi/property.kt index 0cc187c454d..2b2077c72bf 100644 --- a/idea/testData/android/lintQuickfix/targetApi/property.kt +++ b/idea/testData/android/lintQuickfix/targetApi/property.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/property.kt.173 b/idea/testData/android/lintQuickfix/targetApi/property.kt.173 new file mode 100644 index 00000000000..0cc187c454d --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/property.kt.173 @@ -0,0 +1,8 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val VECTOR_DRAWABLE = VectorDrawable() +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/property.kt.expected b/idea/testData/android/lintQuickfix/targetApi/property.kt.expected index 9b8f9a4119e..2103b804754 100644 --- a/idea/testData/android/lintQuickfix/targetApi/property.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/property.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/property.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/property.kt.expected.173 new file mode 100644 index 00000000000..9b8f9a4119e --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/property.kt.expected.173 @@ -0,0 +1,11 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +class VectorDrawableProvider { + val VECTOR_DRAWABLE = VectorDrawable() +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt index 1721eb0a819..1997b7fc223 100644 --- a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt +++ b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(M) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.app.Activity val top: Int diff --git a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.173 b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.173 new file mode 100644 index 00000000000..1721eb0a819 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.173 @@ -0,0 +1,6 @@ +// INTENTION_TEXT: Add @TargetApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +import android.app.Activity + +val top: Int + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected index 869d8ab43ef..ce8347f83f9 100644 --- a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(M) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.app.Activity import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected.173 new file mode 100644 index 00000000000..869d8ab43ef --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected.173 @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Add @TargetApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +import android.annotation.TargetApi +import android.app.Activity +import android.os.Build + +val top: Int + @TargetApi(Build.VERSION_CODES.M) + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/when.kt b/idea/testData/android/lintQuickfix/targetApi/when.kt index 27a59191406..b5a33d8e356 100644 --- a/idea/testData/android/lintQuickfix/targetApi/when.kt +++ b/idea/testData/android/lintQuickfix/targetApi/when.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/when.kt.173 b/idea/testData/android/lintQuickfix/targetApi/when.kt.173 new file mode 100644 index 00000000000..27a59191406 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/when.kt.173 @@ -0,0 +1,14 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + return when (flag) { + true -> VectorDrawable() + else -> VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/when.kt.expected b/idea/testData/android/lintQuickfix/targetApi/when.kt.expected index 4ca486cdcbb..275e59c2bbe 100644 --- a/idea/testData/android/lintQuickfix/targetApi/when.kt.expected +++ b/idea/testData/android/lintQuickfix/targetApi/when.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.annotation.TargetApi import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetApi/when.kt.expected.173 b/idea/testData/android/lintQuickfix/targetApi/when.kt.expected.173 new file mode 100644 index 00000000000..4ca486cdcbb --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/when.kt.expected.173 @@ -0,0 +1,17 @@ +// INTENTION_TEXT: Add @TargetApi(LOLLIPOP) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.annotation.TargetApi +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + val flag = false + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + fun getVectorDrawable(): VectorDrawable { + return when (flag) { + true -> VectorDrawable() + else -> VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt index e052b68c6e6..ff3f7cbec75 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt @@ -1,6 +1,6 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } // INTENTION_NOT_AVAILABLE -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import kotlin.reflect.KClass diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt.173 new file mode 100644 index 00000000000..e052b68c6e6 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/annotation.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INTENTION_NOT_AVAILABLE +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import kotlin.reflect.KClass + +annotation class SomeAnnotationWithClass(val cls: KClass<*>) + +@SomeAnnotationWithClass(VectorDrawable::class) +class VectorDrawableProvider { +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt index 97f5da238fa..6169b9947a8 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt @@ -1,6 +1,6 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } // INTENTION_NOT_AVAILABLE -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt.173 new file mode 100644 index 00000000000..97f5da238fa --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/defaultParameter.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INTENTION_NOT_AVAILABLE +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + + +fun withDefaultParameter(vector: VectorDrawable = VectorDrawable()) { + +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt index 8aee1d9d670..ec9f278cf18 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.app.Activity import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.173 new file mode 100644 index 00000000000..8aee1d9d670 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.app.Activity +import android.graphics.drawable.VectorDrawable + +data class ValueProvider(var p1: VectorDrawable, val p2: Int) + +val activity = Activity() +fun foo() { + val (v1, v2) = ValueProvider(VectorDrawable(), 0) +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected index dd2eb2154bc..23ea52ab746 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.app.Activity import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected.173 new file mode 100644 index 00000000000..dd2eb2154bc --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/destructuringDeclaration.kt.expected.173 @@ -0,0 +1,17 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.app.Activity +import android.graphics.drawable.VectorDrawable +import android.os.Build + +data class ValueProvider(var p1: VectorDrawable, val p2: Int) + +val activity = Activity() +fun foo() { + val (v1, v2) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ValueProvider(VectorDrawable(), 0) + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt index 16ee93fa998..e3b7139ae3c 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.173 new file mode 100644 index 00000000000..16ee93fa998 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.173 @@ -0,0 +1,8 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable = VectorDrawable() +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected index 878163aab0f..4dfc08cfb66 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected.173 new file mode 100644 index 00000000000..878163aab0f --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/expressionBody.kt.expected.173 @@ -0,0 +1,13 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt index 915c5c697a8..403f79dd79a 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.173 new file mode 100644 index 00000000000..915c5c697a8 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + with(this) { + return VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected index a6165526117..09a525dbc6b 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected.173 new file mode 100644 index 00000000000..a6165526117 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/functionLiteral.kt.expected.173 @@ -0,0 +1,17 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + with(this) { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt index 79651954230..7ba6b98af31 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.173 new file mode 100644 index 00000000000..79651954230 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.173 @@ -0,0 +1,7 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +val v: VectorDrawable + get() = VectorDrawable() \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected index 01d2adcc812..ed06e869036 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected.173 new file mode 100644 index 00000000000..01d2adcc812 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/getterWIthExpressionBody.kt.expected.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +val v: VectorDrawable + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt index e2f81f2bbc0..e0224b675e4 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.173 new file mode 100644 index 00000000000..e2f81f2bbc0 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.173 @@ -0,0 +1,12 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + if (flag) + return VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected index 960340e8eca..be38a41256b 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected.173 new file mode 100644 index 00000000000..960340e8eca --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/if.kt.expected.173 @@ -0,0 +1,17 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + if (flag) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt index c74f3ee3ab6..e2f2a4f0ac1 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.173 new file mode 100644 index 00000000000..c74f3ee3ab6 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.173 @@ -0,0 +1,13 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + if (flag) { + return VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected index 224a8f295f8..bda925fbbf2 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected.173 new file mode 100644 index 00000000000..224a8f295f8 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/ifWithBlock.kt.expected.173 @@ -0,0 +1,18 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + if (flag) { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt index febb06d9b65..9c9bf6d37a7 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection class Test { fun foo(): Int { diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.173 new file mode 100644 index 00000000000..febb06d9b65 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.173 @@ -0,0 +1,8 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection + +class Test { + fun foo(): Int { + return android.R.attr.windowTranslucentStatus + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected index 80f478a30d7..b4d5931c5c8 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected @@ -1,7 +1,7 @@ import android.os.Build // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintInlinedApiInspection class Test { fun foo(): Int { diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected.173 new file mode 100644 index 00000000000..80f478a30d7 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/inlinedConstant.kt.expected.173 @@ -0,0 +1,14 @@ +import android.os.Build + +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection + +class Test { + fun foo(): Int { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + android.R.attr.windowTranslucentStatus + } else { + TODO("VERSION.SDK_INT < KITKAT") + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt index a1bbff234ae..49ac9188e6b 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.173 new file mode 100644 index 00000000000..a1bbff234ae --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.173 @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + return VectorDrawable() + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected index 6685b74c231..531645ed71c 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected.173 new file mode 100644 index 00000000000..6685b74c231 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/method.kt.expected.173 @@ -0,0 +1,15 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + fun getVectorDrawable(): VectorDrawable { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt index 11474aca918..b12f04272fc 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.173 new file mode 100644 index 00000000000..11474aca918 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.173 @@ -0,0 +1,14 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + return when (flag) { + true -> VectorDrawable() + else -> VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected index bfe5397edec..e52140da298 100644 --- a/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected @@ -1,5 +1,5 @@ // INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } -// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection import android.graphics.drawable.VectorDrawable import android.os.Build diff --git a/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected.173 b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected.173 new file mode 100644 index 00000000000..bfe5397edec --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetVersionCheck/when.kt.expected.173 @@ -0,0 +1,19 @@ +// INTENTION_TEXT: Surround with if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { ... } +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection + +import android.graphics.drawable.VectorDrawable +import android.os.Build + +class VectorDrawableProvider { + val flag = false + fun getVectorDrawable(): VectorDrawable { + return when (flag) { + true -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + VectorDrawable() + } else { + TODO("VERSION.SDK_INT < LOLLIPOP") + } + else -> VectorDrawable() + } + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt b/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt index aeb948e53f0..4768e2c1abb 100644 --- a/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt +++ b/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt @@ -1,2 +1,2 @@ // ArrayList list = new ArrayList(); -// NO_CONVERSION_EXPECTED \ No newline at end of file +//// NO_CONVERSION_EXPECTED diff --git a/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt.173 b/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt.173 new file mode 100644 index 00000000000..aeb948e53f0 --- /dev/null +++ b/idea/testData/copyPaste/plainTextConversion/IntoComment.expected.kt.173 @@ -0,0 +1,2 @@ +// ArrayList list = new ArrayList(); +// NO_CONVERSION_EXPECTED \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java index 7838151aaca..87faa06cbf5 100644 --- a/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java +++ b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java @@ -1,3 +1,3 @@ package foo; -public class NestedJava {} \ No newline at end of file +public class NestedJava {} diff --git a/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java.173 b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java.173 new file mode 100644 index 00000000000..7838151aaca --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevel/after/foo/NestedJava.java.173 @@ -0,0 +1,3 @@ +package foo; + +public class NestedJava {} \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java index 38714288dad..1ac331ea785 100644 --- a/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java +++ b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java @@ -1,3 +1,3 @@ package bar; -public class NestedJava {} \ No newline at end of file +public class NestedJava {} diff --git a/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java.173 b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java.173 new file mode 100644 index 00000000000..38714288dad --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/callableReferences/nestedToTopLevelAndAnotherPackage/after/bar/NestedJava.java.173 @@ -0,0 +1,3 @@ +package bar; + +public class NestedJava {} \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java index ebc16c54769..ed8a550348b 100644 --- a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java @@ -2,4 +2,4 @@ package b; public class X { -} \ No newline at end of file +} diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java.173 b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java.173 new file mode 100644 index 00000000000..ebc16c54769 --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInAnotherPackage/after/b/X.java.173 @@ -0,0 +1,5 @@ +package b; + +public class X { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java index 76a017d26d4..094432be8a3 100644 --- a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java @@ -2,4 +2,4 @@ package a; public class X { -} \ No newline at end of file +} diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java.173 b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java.173 new file mode 100644 index 00000000000..76a017d26d4 --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackage/after/a/X.java.173 @@ -0,0 +1,5 @@ +package a; + +public class X { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java index 81517efca14..2abd247a9c7 100644 --- a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java @@ -7,4 +7,4 @@ public class X { this.outer = outer; System.out.println(s); } -} \ No newline at end of file +} diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java.173 b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java.173 new file mode 100644 index 00000000000..81517efca14 --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstance/after/a/X.java.173 @@ -0,0 +1,10 @@ +package a; + +public class X { + private A outer; + + public X(A outer, String s) { + this.outer = outer; + System.out.println(s); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java index 1209b9aa3d4..c2cf9a94f8c 100644 --- a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java @@ -9,4 +9,4 @@ public class X { this.outer = outer; System.out.println(f.invoke()); } -} \ No newline at end of file +} diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java.173 b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java.173 new file mode 100644 index 00000000000..1209b9aa3d4 --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndAddOuterInstanceWithLambda/after/a/X.java.173 @@ -0,0 +1,12 @@ +package a; + +import kotlin.jvm.functions.Function0; + +public class X { + private A outer; + + public X(A outer, Function0 f) { + this.outer = outer; + System.out.println(f.invoke()); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java index 9e6d828af80..0bbefa43122 100644 --- a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java @@ -2,4 +2,4 @@ package a; public class Y { -} \ No newline at end of file +} diff --git a/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java.173 b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java.173 new file mode 100644 index 00000000000..9e6d828af80 --- /dev/null +++ b/idea/testData/refactoring/move/java/moveClass/moveInnerToTop/moveNestedClassToTopLevelInTheSamePackageAndRename/after/a/Y.java.173 @@ -0,0 +1,5 @@ +package a; + +public class Y { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/safeDelete/deleteClass/kotlinClass/trait2.kt.messages b/idea/testData/refactoring/safeDelete/deleteClass/kotlinClass/trait2.kt.messages new file mode 100644 index 00000000000..fbc0e43ed1c --- /dev/null +++ b/idea/testData/refactoring/safeDelete/deleteClass/kotlinClass/trait2.kt.messages @@ -0,0 +1 @@ +interface test.A has 1 usage that is not safe to delete. \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt index f910a608643..8b64372ea99 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt @@ -16,8 +16,14 @@ package org.jetbrains.kotlin.asJava +import com.intellij.injected.editor.EditorWindow +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import com.intellij.psi.search.GlobalSearchScope import com.intellij.testFramework.LightProjectDescriptor +import junit.framework.TestCase +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor import org.jetbrains.kotlin.idea.test.PluginTestCaseBase @@ -72,4 +78,58 @@ class KtFileLightClassTest : KotlinLightCodeInsightFixtureTestCase() { override fun getTestDataPath(): String { return PluginTestCaseBase.getTestDataPathBase() + "/asJava/fileLightClass/" } + + fun testInjectedCode() { + myFixture.configureByText("foo.kt", """ + import org.intellij.lang.annotations.Language + + fun foo(@Language("kotlin") a: String){a.toString()} + + fun bar(){ foo("class A") } + """) + + + myFixture.testHighlighting("foo.kt") + + val injectedFile = (editor as? EditorWindow)?.injectedFile + assertEquals("Wrong injection language", "kotlin", injectedFile?.language?.id) + assertEquals("Injected class should be `A`", "A", (injectedFile as KtFile).classes.single().name) + } + + + fun testSameVirtualFileForLightElement() { + + val psiFile = myFixture.addFileToProject( + "pkg/AnnotatedClass.kt", """ + package pkg + + class AnnotatedClass { + @Deprecated("a") + fun bar(param: String) = null + } + """.trimIndent() + ) + + fun lightElement(file: PsiFile): PsiElement = (file as KtFile).classes.single() + .methods.first { it.name == "bar" } + .annotations.first { it.qualifiedName == "kotlin.Deprecated" }.also { + // Otherwise following asserts have no sense + TestCase.assertTrue("psi element should be light ", it is KtLightElement<*, *>) + } + + + val copied = psiFile.copied() + TestCase.assertNull("virtual file for copy should be null", copied.virtualFile) + TestCase.assertNotNull("psi element in copy", lightElement(copied)) + TestCase.assertSame("copy.originalFile should be psiFile", copied.originalFile, psiFile) + + // virtual files should be the same for light and non-light element, + // otherwise we will not be able to find proper module by file from light element + TestCase.assertSame( + "virtualFiles of element and file itself should be the same", + lightElement(copied).containingFile.originalFile.virtualFile, + copied.originalFile.virtualFile + ) + } + } diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt.173 b/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt.173 new file mode 100644 index 00000000000..f910a608643 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtFileLightClassTest.kt.173 @@ -0,0 +1,75 @@ +/* + * 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.asJava + +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile + +class KtFileLightClassTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): LightProjectDescriptor = KotlinLightProjectDescriptor.INSTANCE + + fun testSimple() { + val file = myFixture.configureByText("A.kt", "class C {}\nobject O {}") as KtFile + val classes = file.classes + assertEquals(2, classes.size) + assertEquals("C", classes[0].qualifiedName) + assertEquals("O", classes[1].qualifiedName) + } + + fun testFileClass() { + val file = myFixture.configureByText("A.kt", "fun f() {}") as KtFile + val classes = file.classes + assertEquals(1, classes.size) + assertEquals("AKt", classes[0].qualifiedName) + } + + fun testMultifileClass() { + val file = myFixture.configureByFiles("multifile1.kt", "multifile2.kt")[0] as KtFile + val aClass = file.classes.single() + assertEquals(1, aClass.findMethodsByName("foo", false).size) + assertEquals(1, aClass.findMethodsByName("bar", false).size) + } + + fun testAliasesOnly() { + val file = myFixture.configureByFile("aliasesOnly.kt") as KtFile + val aClass = file.classes.single() + assertEquals(0, aClass.getMethods().size) + } + + fun testNoFacadeForScript() { + val file = myFixture.configureByText("foo.kts", "package foo") as KtFile + assertEquals(0, file.classes.size) + val facadeFiles = KotlinAsJavaSupport.getInstance(project).findFilesForFacade(FqName("foo.FooKt"), GlobalSearchScope.allScope(project)) + assertEquals(0, facadeFiles.size) + } + + fun testNoFacadeForHeaderClass() { + val file = myFixture.configureByText("foo.kt", "header fun foo(): Int") as KtFile + assertEquals(0, file.classes.size) + val facadeFiles = KotlinAsJavaSupport.getInstance(project).findFilesForFacade(FqName("foo.FooKt"), GlobalSearchScope.allScope(project)) + assertEquals(0, facadeFiles.size) + } + + override fun getTestDataPath(): String { + return PluginTestCaseBase.getTestDataPathBase() + "/asJava/fileLightClass/" + } +} diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt index 0df3fdf84e6..fac4a44d2f4 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt @@ -79,7 +79,6 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { val annotation = myFixture.findClass("AnnotatedClass").methods.first { it.name == "bar" }.parameterList.parameters.single() .expectAnnotations(2).single { it.qualifiedName == "Qualifier" } val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement - TestCase.assertTrue(annotationAttributeVal.isPhysical) assertTextRangeAndValue("\"foo\"", "foo", annotationAttributeVal) } @@ -502,6 +501,7 @@ class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { private fun assertTextAndRange(expected: String, psiElement: PsiElement) { TestCase.assertEquals("mismatch for $psiElement of ${psiElement.javaClass}", expected, psiElement.text) TestCase.assertEquals(expected, psiElement.textRange.substring(psiElement.containingFile.text)) + TestCase.assertEquals(psiElement, PsiAnchor.create(psiElement).retrieve()) } private fun assertIsKtLightAnnotation(expected: String, psiElement: PsiElement) { diff --git a/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 new file mode 100644 index 00000000000..0df3fdf84e6 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/asJava/KtLightAnnotationTest.kt.173 @@ -0,0 +1,534 @@ +/* + * 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.asJava + +import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl +import com.intellij.psi.* +import com.intellij.testFramework.LightProjectDescriptor +import junit.framework.TestCase +import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.idea.completion.test.assertInstanceOf +import org.jetbrains.kotlin.idea.facet.configureFacet +import org.jetbrains.kotlin.idea.facet.getOrCreateFacet +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor + +class KtLightAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { + + override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + fun testBooleanAnnotationDefaultValue() { + myFixture.addClass(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + @Target(ElementType.FIELD) + public @interface Autowired { + boolean required() default true; + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + class AnnotatedClass{ + @Autowired + lateinit var bean: String + } + """.trimIndent()) + myFixture.testHighlighting("Autowired.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").fields.single() + .expectAnnotations(2).single { it.qualifiedName == "Autowired" } + val annotationAttributeVal = annotations.findAttributeValue("required") as PsiElement + assertTextRangeAndValue("true", true, annotationAttributeVal) + } + + fun testStringAnnotationWithUnnamedParameter() { + myFixture.addClass(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + @Target(ElementType.PARAMETER) + public @interface Qualifier { + String value(); + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + class AnnotatedClass { + fun bar(@Qualifier("foo") param: String){} + } + """.trimIndent()) + myFixture.testHighlighting("Qualifier.java", "AnnotatedClass.kt") + + val annotation = myFixture.findClass("AnnotatedClass").methods.first { it.name == "bar" }.parameterList.parameters.single() + .expectAnnotations(2).single { it.qualifiedName == "Qualifier" } + val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement + TestCase.assertTrue(annotationAttributeVal.isPhysical) + assertTextRangeAndValue("\"foo\"", "foo", annotationAttributeVal) + } + + fun testAnnotationsInAnnotationsDeclarations() { + myFixture.addClass(""" + public @interface OuterAnnotation { + InnerAnnotation attribute(); + @interface InnerAnnotation { + } + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @OuterAnnotation(attribute = OuterAnnotation.InnerAnnotation()) + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("attribute") as PsiElement + assertTextAndRange("InnerAnnotation()", annotationAttributeVal) + } + + fun testAnnotationsInAnnotationsArrayDeclarations() { + myFixture.addClass(""" + public @interface OuterAnnotation { + InnerAnnotation[] attributes(); + @interface InnerAnnotation { + } + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @OuterAnnotation(attributes = arrayOf(OuterAnnotation.InnerAnnotation())) + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("attributes") as PsiArrayInitializerMemberValue + assertTextAndRange("arrayOf(OuterAnnotation.InnerAnnotation())", annotationAttributeVal) + assertTextAndRange("InnerAnnotation()", annotationAttributeVal.initializers[0]) + } + + + fun testAnnotationsInAnnotationsFinalDeclarations() { + myFixture.addClass(""" + public @interface OuterAnnotation { + InnerAnnotation attribute(); + @interface InnerAnnotation { + } + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @OuterAnnotation(attribute = OuterAnnotation.InnerAnnotation()) + class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("attribute") as PsiElement + assertTextAndRange("InnerAnnotation()", annotationAttributeVal) + } + + fun testAnnotationsInAnnotationsInAnnotationsDeclarations() { + myFixture.addClass(""" + public @interface OuterAnnotation { + InnerAnnotation attribute(); + @interface InnerAnnotation { + InnerInnerAnnotation attribute(); + @interface InnerInnerAnnotation { + } + } + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @OuterAnnotation(attribute = OuterAnnotation.InnerAnnotation(attribute = OuterAnnotation.InnerAnnotation.InnerInnerAnnotation())) + open class AnnotatedClass //There is another exception if class is not open + """.trimIndent()) + myFixture.testHighlighting("OuterAnnotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("attribute") as PsiElement + assertTextAndRange("InnerAnnotation(attribute = OuterAnnotation.InnerAnnotation.InnerInnerAnnotation())", annotationAttributeVal) + + annotationAttributeVal as PsiAnnotation + val innerAnnotationAttributeVal = annotationAttributeVal.findAttributeValue("attribute") as PsiElement + assertTextAndRange("InnerInnerAnnotation()", innerAnnotationAttributeVal) + } + + fun testKotlinAnnotations() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno1(val anno2: Anno2) + annotation class Anno2(val anno3: Anno3) + annotation class Anno3 + + @Anno1(Anno2(Anno3())) + class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("anno2") as PsiElement + assertTextAndRange("Anno2(Anno3())", annotationAttributeVal) + + annotationAttributeVal as PsiAnnotation + val innerAnnotationAttributeVal = annotationAttributeVal.findAttributeValue("anno3") as PsiElement + assertTextAndRange("Anno3()", innerAnnotationAttributeVal) + assertIsKtLightAnnotation("Anno3()", innerAnnotationAttributeVal) + } + + fun testKotlinAnnotationWithStringArray() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno(val params: Array) + @Anno(arrayOf("abc", "def")) + class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("params") as PsiElement + assertTextAndRange("arrayOf(\"abc\", \"def\")", annotationAttributeVal) + + annotationAttributeVal as PsiArrayInitializerMemberValue + assertTextAndRange("\"abc\"", annotationAttributeVal.initializers[0]) + assertTextAndRange("\"def\"", annotationAttributeVal.initializers[1]) + } + + fun testKotlinAnnotationWithStringArrayLiteral() { + configureKotlinVersion("1.2") + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno(val params: Array) + @Anno(params = ["abc", "def"]) + class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("params") as PsiElement + assertTextAndRange("[\"abc\", \"def\"]", annotationAttributeVal) + + annotationAttributeVal as PsiArrayInitializerMemberValue + assertTextAndRange("\"abc\"", annotationAttributeVal.initializers[0].assertInstanceOf()) + assertTextAndRange("\"def\"", annotationAttributeVal.initializers[1]) + } + + + fun testKotlinAnnotationsArray() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno1(val anno2: Array) + annotation class Anno2(val v:Int) + + @Anno1(anno2 = arrayOf(Anno2(1), Anno2(2))) + class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("AnnotatedClass.kt") + + val annotation = myFixture.findClass("AnnotatedClass").expectAnnotations(1).single() + + val annotationAttributeVal = annotation.findAttributeValue("anno2") as PsiArrayInitializerMemberValue + assertTextAndRange("arrayOf(Anno2(1), Anno2(2))", annotationAttributeVal) + annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal -> + assertTextAndRange("Anno2(1)", innerAnnotationAttributeVal) + assertIsKtLightAnnotation("Anno2(1)", innerAnnotationAttributeVal) + innerAnnotationAttributeVal as PsiAnnotation + val value = innerAnnotationAttributeVal.findAttributeValue("v").assertInstanceOf() + assertTextAndRange("1", value) + } + + + val attributeValueFromParameterList = annotation.parameterList.attributes.single().value as PsiArrayInitializerMemberValue + assertTextAndRange("arrayOf(Anno2(1), Anno2(2))", attributeValueFromParameterList) + attributeValueFromParameterList.initializers[0].let { innerAnnotationAttributeVal -> + assertTextAndRange("Anno2(1)", innerAnnotationAttributeVal) + assertIsKtLightAnnotation("Anno2(1)", innerAnnotationAttributeVal) + } + + } + + fun testVarargAnnotation() { + + myFixture.configureByText("Outer.java", """ + @interface Outer{ + Inner[] value() default {}; + } + + @interface Inner{ + } + """) + myFixture.configureByText("AnnotatedClass.kt", """ + @Outer(Inner()) + class MyAnnotated {} + """.trimIndent()) + + val annotations = myFixture.findClass("MyAnnotated").expectAnnotations(1) + annotations[0].let { annotation -> + val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement + assertTextAndRange("(Inner())", annotationAttributeVal) + annotationAttributeVal as PsiArrayInitializerMemberValue + annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal -> + assertTextAndRange("Inner()", innerAnnotationAttributeVal) + assertIsKtLightAnnotation("Inner()", innerAnnotationAttributeVal) + } + } + + } + + fun testVarargWithSpread() { + myFixture.addClass(""" + public @interface Annotation { + String[] value(); + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @Annotation(value = *arrayOf("a", "b", "c")) + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("arrayOf(\"a\", \"b\", \"c\")", annotationAttributeVal) + for ((i, arg) in listOf("\"a\"", "\"b\"", "\"c\"").withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + fun testVarargWithSpreadComplex() { + myFixture.addClass(""" + public @interface Annotation { + String[] value(); + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @Annotation(value = arrayOf(*arrayOf("a", "b"), "c", *arrayOf("d", "e"))) + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("arrayOf(*arrayOf(\"a\", \"b\"), \"c\", *arrayOf(\"d\", \"e\"))", annotationAttributeVal) + for ((i, arg) in listOf("arrayOf(\"a\", \"b\")", "\"c\"", "arrayOf(\"d\", \"e\")").withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + fun testVarargWithArrayLiteral() { + myFixture.addClass(""" + public @interface Annotation { + String[] value(); + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @Annotation(value = ["a", "b", "c"]) + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("[\"a\", \"b\", \"c\"]", annotationAttributeVal) + for ((i, arg) in listOf("\"a\"", "\"b\"", "\"c\"").withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + fun testVarargWithSingleArg() { + myFixture.addClass(""" + public @interface Annotation { + String[] value(); + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @Annotation(value = "a") + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("\"a\"", annotationAttributeVal) + for ((i, arg) in listOf("\"a\"").withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + fun testVarargWithArrayLiteralAndSpread() { + myFixture.addClass(""" + public @interface Annotation { + String[] value(); + } + """.trimIndent()) + + myFixture.configureByText("AnnotatedClass.kt", """ + @Annotation(*["a", "b", "c"]) + open class AnnotatedClass + """.trimIndent()) + myFixture.testHighlighting("Annotation.java", "AnnotatedClass.kt") + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotationAttributeVal = annotations.first().findAttributeValue("value") as PsiArrayInitializerMemberValue + assertTextAndRange("[\"a\", \"b\", \"c\"]", annotationAttributeVal) + for ((i, arg) in listOf("\"a\"", "\"b\"", "\"c\"").withIndex()) { + assertTextAndRange(arg, annotationAttributeVal.initializers[i]) + } + } + + fun testRepeatableAnnotationsArray() { + + myFixture.configureByText("RAnno.java", """ + import java.lang.annotation.Repeatable; + + @interface RContainer{ + RAnno[] value() default {}; + } + + @Repeatable(RContainer.class) + public @interface RAnno { + String[] value() default {}; + } + """) + myFixture.configureByText("AnnotatedClass.kt", """ + @RAnno() + @RAnno("1") + @RAnno("1", "2") + class AnnotatedClass + """.trimIndent()) + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(3) + annotations[0].let { annotation -> + val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement + assertTextAndRange("{}", annotationAttributeVal) + annotationAttributeVal as PsiArrayInitializerMemberValue + TestCase.assertTrue(annotationAttributeVal.initializers.isEmpty()) + } + annotations[1].let { annotation -> + val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement + assertTextAndRange("(\"1\")", annotationAttributeVal) + annotationAttributeVal as PsiArrayInitializerMemberValue + annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal -> + assertTextAndRange("\"1\"", innerAnnotationAttributeVal) + } + } + annotations[2].let { annotation -> + val annotationAttributeVal = annotation.findAttributeValue("value") as PsiElement + assertTextAndRange("(\"1\", \"2\")", annotationAttributeVal) + annotationAttributeVal as PsiArrayInitializerMemberValue + annotationAttributeVal.initializers[0].let { innerAnnotationAttributeVal -> + assertTextAndRange("\"1\"", innerAnnotationAttributeVal) + } + annotationAttributeVal.initializers[1].let { innerAnnotationAttributeVal -> + assertTextAndRange("\"2\"", innerAnnotationAttributeVal) + } + } + + } + + fun testWrongNamesPassed() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno1(val i:Int , val j: Int) + + @Anno1(k = 3, l = 5) + class AnnotatedClass + """.trimIndent()) + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotation = annotations.first() + TestCase.assertNull(annotation.findAttributeValue("k")) + TestCase.assertNull(annotation.findAttributeValue("l")) + TestCase.assertNull(annotation.findAttributeValue("i")) + TestCase.assertNull(annotation.findAttributeValue("j")) + } + + fun testWrongValuesPassed() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno1(val i: Int , val j: Int) + + @Anno1(i = true, j = false) + class AnnotatedClass + """.trimIndent()) + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotation = annotations.first() + assertTextAndRange("true", annotation.findAttributeValue("i")!!) + assertTextAndRange("false", annotation.findAttributeValue("j")!!) + } + + fun testDuplicateParameters() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno1(val i:Int , val i: Boolean) + + @Anno1(i = true, i = 3) + class AnnotatedClass + """.trimIndent()) + + val annotations = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + val annotation = annotations.first() + assertTextAndRange("", annotation.findAttributeValue("i")!!) + } + + fun testMissingDefault() { + myFixture.configureByText("AnnotatedClass.kt", """ + annotation class Anno1(val i: Int = 0) + + @Anno1() + class AnnotatedClass + """.trimIndent()) + + val (annotation) = myFixture.findClass("AnnotatedClass").expectAnnotations(1) + assertTextAndRange("0", annotation.findAttributeValue("i")!!) + } + + private fun assertTextAndRange(expected: String, psiElement: PsiElement) { + TestCase.assertEquals("mismatch for $psiElement of ${psiElement.javaClass}", expected, psiElement.text) + TestCase.assertEquals(expected, psiElement.textRange.substring(psiElement.containingFile.text)) + } + + private fun assertIsKtLightAnnotation(expected: String, psiElement: PsiElement) { + TestCase.assertEquals(expected, (psiElement as KtLightAnnotationForSourceEntry).kotlinOrigin.text) + } + + private fun assertTextRangeAndValue(expected: String, value: Any?, psiElement: PsiElement) { + assertTextAndRange(expected, psiElement) + val result = JavaPsiFacade.getInstance(project).constantEvaluationHelper.computeConstantExpression(psiElement) + TestCase.assertEquals(value, result) + val smartPointer = SmartPointerManager.getInstance(psiElement.project).createSmartPsiElementPointer(psiElement) + assertTextAndRange(expected, smartPointer.element!!) + } + + private fun PsiModifierListOwner.expectAnnotations(number: Int): Array = + this.modifierList!!.annotations.apply { + TestCase.assertEquals("expected one annotation, found ${this.joinToString(", ") { it.qualifiedName ?: "unknown" }}", + number, size) + } + + private fun configureKotlinVersion(version: String) { + WriteAction.run { + val modelsProvider = IdeModifiableModelsProviderImpl(project) + val facet = module.getOrCreateFacet(modelsProvider, useProjectSettings = false) + facet.configureFacet(version, LanguageFeature.State.DISABLED, null, modelsProvider) + modelsProvider.commit() + } + } + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt index 1a4a730db83..38bd43f41c6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt @@ -8,13 +8,13 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.openapi.util.io.FileUtil import org.junit.Assert import java.io.File -import java.io.IOException +import java.nio.file.Path abstract class AbstractConfigureKotlinInTempDirTest : AbstractConfigureKotlinTest() { - @Throws(IOException::class) - override fun getIprFile(): File { + override fun getProjectDirOrFile(): Path { val tempDir = FileUtil.generateRandomTemporaryPath() FileUtil.createTempDirectory("temp", null) + myFilesToDelete.add(tempDir) FileUtil.copyDir(File(projectRoot), tempDir) @@ -24,8 +24,9 @@ abstract class AbstractConfigureKotlinInTempDirTest : AbstractConfigureKotlinTes if (!File(projectFilePath).exists()) { val dotIdeaPath = projectRoot + "/.idea" Assert.assertTrue("Project file or '.idea' dir should exists in " + projectRoot, File(dotIdeaPath).exists()) - return File(projectRoot) + return File(projectRoot).toPath() } - return File(projectFilePath) + + return File(projectFilePath).toPath() } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt.173 new file mode 100644 index 00000000000..1a4a730db83 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinInTempDirTest.kt.173 @@ -0,0 +1,31 @@ +/* + * 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.idea.configuration + +import com.intellij.openapi.util.io.FileUtil +import org.junit.Assert +import java.io.File +import java.io.IOException + +abstract class AbstractConfigureKotlinInTempDirTest : AbstractConfigureKotlinTest() { + @Throws(IOException::class) + override fun getIprFile(): File { + val tempDir = FileUtil.generateRandomTemporaryPath() + FileUtil.createTempDirectory("temp", null) + + FileUtil.copyDir(File(projectRoot), tempDir) + + val projectRoot = tempDir.path + + val projectFilePath = projectRoot + "/projectFile.ipr" + if (!File(projectFilePath).exists()) { + val dotIdeaPath = projectRoot + "/.idea" + Assert.assertTrue("Project file or '.idea' dir should exists in " + projectRoot, File(dotIdeaPath).exists()) + return File(projectRoot) + } + return File(projectFilePath) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt index ffa6c40d6ce..46f2a10fae6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt @@ -35,7 +35,7 @@ import org.jetbrains.kotlin.idea.framework.KotlinSdkType import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.utils.PathUtil import java.io.File -import java.io.IOException +import java.nio.file.Path abstract class AbstractConfigureKotlinTest : PlatformTestCase() { override fun setUp() { @@ -126,16 +126,14 @@ abstract class AbstractConfigureKotlinTest : PlatformTestCase() { val modules: Array get() = ModuleManager.getInstance(myProject).modules - @Throws(IOException::class) - override fun getIprFile(): File { + override fun getProjectDirOrFile(): Path { val projectFilePath = projectRoot + "/projectFile.ipr" TestCase.assertTrue("Project file should exists " + projectFilePath, File(projectFilePath).exists()) - return File(projectFilePath) + return File(projectFilePath).toPath() } - @Throws(Exception::class) - override fun doCreateProject(projectFile: File): Project? { - return myProjectManager.loadProject(projectFile.path) + override fun doCreateProject(projectFile: Path): Project { + return myProjectManager.loadProject(projectFile.toFile().path)!! } private val projectName: String diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt.173 new file mode 100644 index 00000000000..ffa6c40d6ce --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/AbstractConfigureKotlinTest.kt.173 @@ -0,0 +1,265 @@ +/* + * 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.configuration + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.PathMacros +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess +import com.intellij.testFramework.PlatformTestCase +import com.intellij.testFramework.UsefulTestCase +import junit.framework.TestCase +import org.jetbrains.kotlin.idea.configuration.KotlinWithLibraryConfigurator.FileState +import org.jetbrains.kotlin.idea.framework.KotlinSdkType +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File +import java.io.IOException + +abstract class AbstractConfigureKotlinTest : PlatformTestCase() { + override fun setUp() { + super.setUp() + + val distPaths = with(PathUtil.kotlinPathsForIdeaPlugin) { + listOf( + stdlibPath, + stdlibSourcesPath, + reflectPath, + kotlinTestPath, + jsKotlinTestJarPath, + jsStdLibJarPath, + jsStdLibSrcJarPath + ) + } + + for (path in distPaths) { + VfsRootAccess.allowRootAccess(testRootDisposable, path.absolutePath) + } + } + + @Throws(Exception::class) + override fun tearDown() { + PathMacros.getInstance().removeMacro(TEMP_DIR_MACRO_KEY) + + super.tearDown() + } + + @Throws(Exception::class) + override fun initApplication() { + super.initApplication() + + KotlinSdkType.setUpIfNeeded() + + ApplicationManager.getApplication().runWriteAction { + ProjectJdkTable.getInstance().addJdk(PluginTestCaseBase.mockJdk6()) + ProjectJdkTable.getInstance().addJdk(PluginTestCaseBase.mockJdk8()) + ProjectJdkTable.getInstance().addJdk(PluginTestCaseBase.mockJdk9()) + } + + PluginTestCaseBase.clearSdkTable(testRootDisposable) + + val tempLibDir = FileUtil.createTempDirectory("temp", null) + PathMacros.getInstance().setMacro(TEMP_DIR_MACRO_KEY, FileUtilRt.toSystemDependentName(tempLibDir.absolutePath)) + } + + protected fun doTestConfigureModulesWithNonDefaultSetup(configurator: KotlinWithLibraryConfigurator) { + assertNoFilesInDefaultPaths() + + val modules = modules + for (module in modules) { + assertNotConfigured(module, configurator) + } + + configurator.configure(myProject, emptyList()) + + assertNoFilesInDefaultPaths() + + for (module in modules) { + assertProperlyConfigured(module, configurator) + } + } + + protected fun doTestOneJavaModule(jarState: FileState) { + doTestOneModule(jarState, JAVA_CONFIGURATOR) + } + + protected fun doTestOneJsModule(jarState: FileState) { + doTestOneModule(jarState, JS_CONFIGURATOR) + } + + private fun doTestOneModule(jarState: FileState, configurator: KotlinWithLibraryConfigurator) { + val module = module + + assertNotConfigured(module, configurator) + configure(module, jarState, configurator) + assertProperlyConfigured(module, configurator) + } + + override fun getModule(): Module { + val modules = ModuleManager.getInstance(myProject).modules + assert(modules.size == 1) { "One module should be loaded " + modules.size } + myModule = modules[0] + return super.getModule() + } + + val modules: Array + get() = ModuleManager.getInstance(myProject).modules + + @Throws(IOException::class) + override fun getIprFile(): File { + val projectFilePath = projectRoot + "/projectFile.ipr" + TestCase.assertTrue("Project file should exists " + projectFilePath, File(projectFilePath).exists()) + return File(projectFilePath) + } + + @Throws(Exception::class) + override fun doCreateProject(projectFile: File): Project? { + return myProjectManager.loadProject(projectFile.path) + } + + private val projectName: String + get() { + val testName = getTestName(true) + return if (testName.contains("_")) { + testName.substring(0, testName.indexOf("_")) + } + else testName + } + + protected val projectRoot: String + get() = BASE_PATH + projectName + + override fun setUpModule() {} + + private fun assertNoFilesInDefaultPaths() { + UsefulTestCase.assertDoesntExist(File(JAVA_CONFIGURATOR.getDefaultPathToJarFile(project))) + UsefulTestCase.assertDoesntExist(File(JS_CONFIGURATOR.getDefaultPathToJarFile(project))) + } + + companion object { + private val BASE_PATH = "idea/testData/configuration/" + private val TEMP_DIR_MACRO_KEY = "TEMP_TEST_DIR" + protected val JAVA_CONFIGURATOR: KotlinJavaModuleConfigurator by lazy { + object : KotlinJavaModuleConfigurator() { + override fun getDefaultPathToJarFile(project: Project) = getPathRelativeToTemp("default_jvm_lib") + } + } + + protected val JS_CONFIGURATOR: KotlinJsModuleConfigurator by lazy { + object : KotlinJsModuleConfigurator() { + override fun getDefaultPathToJarFile(project: Project) = getPathRelativeToTemp("default_js_lib") + } + } + + private fun configure( + modules: List, + runtimeState: FileState, + configurator: KotlinWithLibraryConfigurator, + jarFromDist: String, + jarFromTemp: String + ) { + val project = modules.first().project + val collector = createConfigureKotlinNotificationCollector(project) + + val pathToJar = getPathToJar(runtimeState, jarFromDist, jarFromTemp) + for (module in modules) { + configurator.configureModule(module, pathToJar, pathToJar, collector, runtimeState) + } + collector.showNotification() + } + + private fun getPathToJar(runtimeState: FileState, jarFromDist: String, jarFromTemp: String) = when (runtimeState) { + KotlinWithLibraryConfigurator.FileState.EXISTS -> jarFromDist + KotlinWithLibraryConfigurator.FileState.COPY -> jarFromTemp + KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY -> jarFromDist + } + + protected fun configure(module: Module, jarState: FileState, configurator: KotlinProjectConfigurator) { + if (configurator is KotlinJavaModuleConfigurator) { + configure(listOf(module), jarState, + configurator as KotlinWithLibraryConfigurator, + pathToExistentRuntimeJar, pathToNonexistentRuntimeJar) + } + if (configurator is KotlinJsModuleConfigurator) { + configure(listOf(module), jarState, + configurator as KotlinWithLibraryConfigurator, + pathToExistentJsJar, pathToNonexistentJsJar) + } + } + + private val pathToNonexistentRuntimeJar: String + get() { + val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/kotlin-runtime.jar" + PlatformTestCase.myFilesToDelete.add(File(pathToTempKotlinRuntimeJar)) + return pathToTempKotlinRuntimeJar + } + + private val pathToNonexistentJsJar: String + get() { + val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.JS_LIB_JAR_NAME + PlatformTestCase.myFilesToDelete.add(File(pathToTempKotlinRuntimeJar)) + return pathToTempKotlinRuntimeJar + } + + private val pathToExistentRuntimeJar: String + get() = PathUtil.kotlinPathsForDistDirectory.stdlibPath.parent + + private val pathToExistentJsJar: String + get() = PathUtil.kotlinPathsForDistDirectory.jsStdLibJarPath.parent + + protected fun assertNotConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) { + TestCase.assertFalse( + String.format("Module %s should not be configured as %s Module", module.name, configurator.presentableText), + configurator.isConfigured(module)) + } + + protected fun assertConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) { + TestCase.assertTrue(String.format("Module %s should be configured with configurator '%s'", module.name, + configurator.presentableText), + configurator.isConfigured(module)) + } + + protected fun assertProperlyConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) { + assertConfigured(module, configurator) + assertNotConfigured(module, getOppositeConfigurator(configurator)) + } + + private fun getOppositeConfigurator(configurator: KotlinWithLibraryConfigurator): KotlinWithLibraryConfigurator { + if (configurator === JAVA_CONFIGURATOR) return JS_CONFIGURATOR + if (configurator === JS_CONFIGURATOR) return JAVA_CONFIGURATOR + + throw IllegalArgumentException("Only JS_CONFIGURATOR and JAVA_CONFIGURATOR are supported") + } + + private fun getPathRelativeToTemp(relativePath: String): String { + val tempPath = PathMacros.getInstance().getValue(TEMP_DIR_MACRO_KEY) + return tempPath + '/' + relativePath + } + } + + override fun getTestProjectJdk(): Sdk { + val projectRootManager = ProjectRootManager.getInstance(project) + return projectRootManager.projectSdk ?: throw IllegalStateException("SDK ${projectRootManager.projectSdkName} was not found") + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt index d4eb4b8ce50..62f47fa44f1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.impl.ApplicationImpl +import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider import org.jetbrains.kotlin.config.LanguageVersion @@ -25,13 +26,15 @@ import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgu import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.junit.Assert +import java.io.File import java.io.IOException +import java.nio.file.Path open class ConfigureKotlinInTempDirTest : AbstractConfigureKotlinInTempDirTest() { @Throws(IOException::class) fun testNoKotlincExistsNoSettingsRuntime10() { val application = ApplicationManager.getApplication() as ApplicationImpl - application.doNotSave(false) + application.isSaveAllowed = true Assert.assertEquals(LanguageVersion.KOTLIN_1_0, module.languageVersionSettings.languageVersion) Assert.assertEquals(LanguageVersion.KOTLIN_1_0, myProject.getLanguageVersionSettings(null).languageVersion) application.saveAll() @@ -40,7 +43,7 @@ open class ConfigureKotlinInTempDirTest : AbstractConfigureKotlinInTempDirTest() fun testNoKotlincExistsNoSettingsLatestRuntime() { val application = ApplicationManager.getApplication() as ApplicationImpl - application.doNotSave(false) + application.isSaveAllowed = true Assert.assertEquals(LanguageVersion.LATEST_STABLE, module.languageVersionSettings.languageVersion) Assert.assertEquals(LanguageVersion.LATEST_STABLE, myProject.getLanguageVersionSettings(null).languageVersion) application.saveAll() @@ -49,7 +52,7 @@ open class ConfigureKotlinInTempDirTest : AbstractConfigureKotlinInTempDirTest() fun testKotlincExistsNoSettingsLatestRuntimeNoVersionAutoAdvance() { val application = ApplicationManager.getApplication() as ApplicationImpl - application.doNotSave(false) + application.isSaveAllowed = true Assert.assertEquals(LanguageVersion.LATEST_STABLE, module.languageVersionSettings.languageVersion) Assert.assertEquals(LanguageVersion.LATEST_STABLE, myProject.getLanguageVersionSettings(null).languageVersion) KotlinCommonCompilerArgumentsHolder.getInstance(project).update { @@ -62,7 +65,7 @@ open class ConfigureKotlinInTempDirTest : AbstractConfigureKotlinInTempDirTest() fun testDropKotlincOnVersionAutoAdvance() { val application = ApplicationManager.getApplication() as ApplicationImpl - application.doNotSave(false) + application.isSaveAllowed = true Assert.assertEquals(LanguageVersion.LATEST_STABLE, module.languageVersionSettings.languageVersion) KotlinCommonCompilerArgumentsHolder.getInstance(project).update { autoAdvanceLanguageVersion = true @@ -97,7 +100,7 @@ open class ConfigureKotlinInTempDirTest : AbstractConfigureKotlinInTempDirTest() fun testLoadAndSaveProjectWithV2FacetConfig() { val moduleFileContentBefore = String(module.moduleFile!!.contentsToByteArray()) val application = ApplicationManager.getApplication() as ApplicationImpl - application.doNotSave(false) + application.isSaveAllowed = true application.saveAll() val moduleFileContentAfter = String(module.moduleFile!!.contentsToByteArray()) Assert.assertEquals(moduleFileContentBefore, moduleFileContentAfter) diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt.173 new file mode 100644 index 00000000000..d4eb4b8ce50 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInTempDirTest.kt.173 @@ -0,0 +1,116 @@ +/* + * 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.application.ApplicationManager +import com.intellij.openapi.application.impl.ApplicationImpl +import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings +import org.jetbrains.kotlin.idea.project.languageVersionSettings +import org.junit.Assert +import java.io.IOException + +open class ConfigureKotlinInTempDirTest : AbstractConfigureKotlinInTempDirTest() { + @Throws(IOException::class) + fun testNoKotlincExistsNoSettingsRuntime10() { + val application = ApplicationManager.getApplication() as ApplicationImpl + application.doNotSave(false) + Assert.assertEquals(LanguageVersion.KOTLIN_1_0, module.languageVersionSettings.languageVersion) + Assert.assertEquals(LanguageVersion.KOTLIN_1_0, myProject.getLanguageVersionSettings(null).languageVersion) + application.saveAll() + Assert.assertTrue(project.baseDir.findFileByRelativePath(".idea/kotlinc.xml") == null) + } + + fun testNoKotlincExistsNoSettingsLatestRuntime() { + val application = ApplicationManager.getApplication() as ApplicationImpl + application.doNotSave(false) + Assert.assertEquals(LanguageVersion.LATEST_STABLE, module.languageVersionSettings.languageVersion) + Assert.assertEquals(LanguageVersion.LATEST_STABLE, myProject.getLanguageVersionSettings(null).languageVersion) + application.saveAll() + Assert.assertTrue(project.baseDir.findFileByRelativePath(".idea/kotlinc.xml") == null) + } + + fun testKotlincExistsNoSettingsLatestRuntimeNoVersionAutoAdvance() { + val application = ApplicationManager.getApplication() as ApplicationImpl + application.doNotSave(false) + Assert.assertEquals(LanguageVersion.LATEST_STABLE, module.languageVersionSettings.languageVersion) + Assert.assertEquals(LanguageVersion.LATEST_STABLE, myProject.getLanguageVersionSettings(null).languageVersion) + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { + autoAdvanceLanguageVersion = false + autoAdvanceApiVersion = false + } + application.saveAll() + Assert.assertTrue(project.baseDir.findFileByRelativePath(".idea/kotlinc.xml") != null) + } + + fun testDropKotlincOnVersionAutoAdvance() { + val application = ApplicationManager.getApplication() as ApplicationImpl + application.doNotSave(false) + Assert.assertEquals(LanguageVersion.LATEST_STABLE, module.languageVersionSettings.languageVersion) + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { + autoAdvanceLanguageVersion = true + autoAdvanceApiVersion = true + } + application.saveAll() + Assert.assertTrue(project.baseDir.findFileByRelativePath(".idea/kotlinc.xml") == null) + } + + fun testProject106InconsistentVersionInConfig() { + val settings = KotlinFacetSettingsProvider.getInstance(myProject).getInitializedSettings(module) + Assert.assertEquals(false, settings.useProjectSettings) + Assert.assertEquals("1.0", settings.languageLevel!!.description) + Assert.assertEquals("1.0", settings.apiLevel!!.description) + } + + fun testProject107InconsistentVersionInConfig() { + val settings = KotlinFacetSettingsProvider.getInstance(myProject).getInitializedSettings(module) + Assert.assertEquals(false, settings.useProjectSettings) + Assert.assertEquals("1.0", settings.languageLevel!!.description) + Assert.assertEquals("1.0", settings.apiLevel!!.description) + } + + fun testFacetWithProjectSettings() { + val settings = KotlinFacetSettingsProvider.getInstance(myProject).getInitializedSettings(module) + Assert.assertEquals(true, settings.useProjectSettings) + Assert.assertEquals("1.1", settings.languageLevel!!.description) + Assert.assertEquals("1.1", settings.apiLevel!!.description) + Assert.assertEquals("-version -Xallow-kotlin-package -Xskip-metadata-version-check", settings.compilerSettings!!.additionalArguments) + } + + fun testLoadAndSaveProjectWithV2FacetConfig() { + val moduleFileContentBefore = String(module.moduleFile!!.contentsToByteArray()) + val application = ApplicationManager.getApplication() as ApplicationImpl + application.doNotSave(false) + application.saveAll() + val moduleFileContentAfter = String(module.moduleFile!!.contentsToByteArray()) + Assert.assertEquals(moduleFileContentBefore, moduleFileContentAfter) + } + + fun testApiVersionWithoutLanguageVersion() { + KotlinCommonCompilerArgumentsHolder.getInstance(myProject) + val settings = myProject.getLanguageVersionSettings() + Assert.assertEquals(ApiVersion.KOTLIN_1_1, settings.apiVersion) + } + + //todo[Sedunov]: wait for fix in platform to avoid misunderstood from Java newbies (also PluginStartupComponent) + /*fun testKotlinSdkAdded() { + Assert.assertTrue(ProjectJdkTable.getInstance().allJdks.any { it.sdkType is KotlinSdkType }) + }*/ +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt index 0dc5c73e8c9..58a932fa6b8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt @@ -18,9 +18,9 @@ package org.jetbrains.kotlin.idea.conversion.copy import com.intellij.openapi.actionSystem.IdeActions import org.jetbrains.kotlin.idea.AbstractCopyPasteTest +import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.idea.test.PluginTestCaseBase -import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils import java.io.File @@ -43,7 +43,7 @@ abstract class AbstractJavaToKotlinCopyPasteConversionTest : AbstractCopyPasteTe } override fun tearDown() { - KotlinEditorOptions.getInstance().loadState(oldEditorOptions) + oldEditorOptions?.let { KotlinEditorOptions.getInstance().loadState(it) } super.tearDown() } diff --git a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt.173 new file mode 100644 index 00000000000..0dc5c73e8c9 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractJavaToKotlinCopyPasteConversionTest.kt.173 @@ -0,0 +1,74 @@ +/* + * 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.conversion.copy + +import com.intellij.openapi.actionSystem.IdeActions +import org.jetbrains.kotlin.idea.AbstractCopyPasteTest +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File +import kotlin.test.assertEquals + +abstract class AbstractJavaToKotlinCopyPasteConversionTest : AbstractCopyPasteTest() { + private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/conversion" + + private var oldEditorOptions: KotlinEditorOptions? = null + + override fun getTestDataPath() = BASE_PATH + + override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + override fun setUp() { + super.setUp() + oldEditorOptions = KotlinEditorOptions.getInstance().state + KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion = true + KotlinEditorOptions.getInstance().isDonTShowConversionDialog = true + } + + override fun tearDown() { + KotlinEditorOptions.getInstance().loadState(oldEditorOptions) + super.tearDown() + } + + fun doTest(path: String) { + myFixture.testDataPath = BASE_PATH + val testName = getTestName(false) + myFixture.configureByFiles(testName + ".java") + + val fileText = myFixture.editor.document.text + val noConversionExpected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NO_CONVERSION_EXPECTED").isNotEmpty() + + myFixture.performEditorAction(IdeActions.ACTION_COPY) + + configureByDependencyIfExists(testName + ".dependency.kt") + configureByDependencyIfExists(testName + ".dependency.java") + + configureTargetFile(testName + ".to.kt") + + ConvertJavaCopyPasteProcessor.conversionPerformed = false + + myFixture.performEditorAction(IdeActions.ACTION_PASTE) + + assertEquals(noConversionExpected, !ConvertJavaCopyPasteProcessor.conversionPerformed, + if (noConversionExpected) "Conversion to Kotlin should not be suggested" else "No conversion to Kotlin suggested") + + KotlinTestUtils.assertEqualsToFile(File(path.replace(".java", ".expected.kt")), myFixture.file.text) + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt index 4e2f9ade559..d825c8c1d6b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt @@ -42,7 +42,7 @@ abstract class AbstractTextJavaToKotlinCopyPasteConversionTest : AbstractCopyPas } override fun tearDown() { - KotlinEditorOptions.getInstance().loadState(oldEditorOptions) + oldEditorOptions?.let { KotlinEditorOptions.getInstance().loadState(it) } super.tearDown() } diff --git a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt.173 new file mode 100644 index 00000000000..4e2f9ade559 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/AbstractTextJavaToKotlinCopyPasteConversionTest.kt.173 @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2016 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.conversion.copy + +import com.intellij.openapi.actionSystem.IdeActions +import org.jetbrains.kotlin.idea.AbstractCopyPasteTest +import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +abstract class AbstractTextJavaToKotlinCopyPasteConversionTest : AbstractCopyPasteTest() { + private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/plainTextConversion" + + private var oldEditorOptions: KotlinEditorOptions? = null + + override fun getTestDataPath() = BASE_PATH + + override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + override fun setUp() { + super.setUp() + oldEditorOptions = KotlinEditorOptions.getInstance().state + KotlinEditorOptions.getInstance().isEnableJavaToKotlinConversion = true + KotlinEditorOptions.getInstance().isDonTShowConversionDialog = true + } + + override fun tearDown() { + KotlinEditorOptions.getInstance().loadState(oldEditorOptions) + super.tearDown() + } + + fun doTest(path: String) { + myFixture.testDataPath = BASE_PATH + val testName = getTestName(false) + myFixture.configureByFiles(testName + ".txt") + + val fileText = myFixture.editor.document.text + val noConversionExpected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NO_CONVERSION_EXPECTED").isNotEmpty() + + myFixture.editor.selectionModel.setSelection(0, fileText.length) + myFixture.performEditorAction(IdeActions.ACTION_COPY) + + configureByDependencyIfExists(testName + ".dependency.kt") + configureByDependencyIfExists(testName + ".dependency.java") + + configureTargetFile(testName + ".to.kt") + + ConvertTextJavaCopyPasteProcessor.conversionPerformed = false + + myFixture.performEditorAction(IdeActions.ACTION_PASTE) + + kotlin.test.assertEquals(noConversionExpected, !ConvertTextJavaCopyPasteProcessor.conversionPerformed, + if (noConversionExpected) "Conversion to Kotlin should not be suggested" else "No conversion to Kotlin suggested") + + KotlinTestUtils.assertEqualsToFile(File(path.replace(".txt", ".expected.kt")), myFixture.file.text) + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java index 81787ed5741..dc780ad2c90 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java @@ -130,36 +130,6 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase { super.setUp(); } - @Override - protected void runTest() throws Throwable { - super.runTest(); - if(getDebugProcess() != null) { - getDebugProcess().getProcessHandler().startNotify(); - waitProcess(getDebugProcess().getProcessHandler()); - waitForCompleted(); - //disposeSession(myDebuggerSession); - assertNull(DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(getDebugProcess().getProcessHandler())); - myDebuggerSession = null; - } - - if (getChecker().contains("JVMTI_ERROR_WRONG_PHASE(112)")) { - myRestart.incrementAndGet(); - if (needsRestart()) { - return; - } - } else { - myRestart.set(0); - } - - throwExceptionsIfAny(); - checkTestOutput(); - } - - private boolean needsRestart() { - int restart = myRestart.get(); - return restart > 0 && restart <= 3; - } - private static void deleteLocalCacheDirectory(boolean assertDeleteSuccess) { System.out.println("-- Remove local cache directory --"); boolean deleteResult = FilesKt.deleteRecursively(LOCAL_CACHE_DIR); diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java.173 b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java.173 new file mode 100644 index 00000000000..81787ed5741 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestCase.java.173 @@ -0,0 +1,400 @@ +/* + * Copyright 2010-2016 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.debugger; + +import com.google.common.collect.Lists; +import com.intellij.compiler.impl.CompilerUtil; +import com.intellij.debugger.DebuggerManagerEx; +import com.intellij.debugger.impl.DescriptorTestCase; +import com.intellij.debugger.impl.OutputChecker; +import com.intellij.execution.ExecutionTestCase; +import com.intellij.execution.configurations.JavaParameters; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.testFramework.EdtTestUtil; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.xdebugger.XDebugSession; +import kotlin.io.FilesKt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage; +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade; +import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime; +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil; +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; +import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.MockLibraryUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.jetbrains.kotlin.test.util.JetTestUtilsKt; +import org.jetbrains.kotlin.utils.ExceptionUtilsKt; +import org.jetbrains.kotlin.utils.PathUtil; +import org.junit.Assert; +import org.junit.ComparisonFailure; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public abstract class KotlinDebuggerTestCase extends DescriptorTestCase { + private static final String TINY_APP = PluginTestCaseBase.getTestDataPathBase() + "/debugger/tinyApp"; + private static final File TINY_APP_SRC = new File(TINY_APP, "src"); + private static boolean IS_TINY_APP_COMPILED = false; + + // Caches are auto-invalidated when file modification in TINY_APP_SRC detected (through File.lastModified()). + // LOCAL_CACHE_DIR removing can be used to force caches invalidating as well. + private static final boolean LOCAL_CACHE_REUSE = true; + + private static final File LOCAL_CACHE_DIR = new File("out/debuggerTinyApp"); + private static final File LOCAL_CACHE_JAR_DIR = new File(LOCAL_CACHE_DIR, "jar"); + private static final File LOCAL_CACHE_APP_DIR = new File(LOCAL_CACHE_DIR, "app"); + private static final File LOCAL_CACHE_LAST_MODIFIED_FILE = new File(LOCAL_CACHE_DIR, "lastModified.txt"); + + private static File CUSTOM_LIBRARY_JAR; + private static final File CUSTOM_LIBRARY_SOURCES = + new File(PluginTestCaseBase.getTestDataPathBase() + "/debugger/customLibraryForTinyApp"); + + protected static final String KOTLIN_LIBRARY_NAME = "KotlinLibrary"; + private static final String CUSTOM_LIBRARY_NAME = "CustomLibrary"; + + @Override + protected OutputChecker initOutputChecker() { + return new KotlinOutputChecker( + this.getClass().getAnnotation(TestMetadata.class).value(), getTestAppPath(), getAppOutputPath()); + } + + @NotNull + @Override + protected String getTestAppPath() { + return TINY_APP; + } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @Override + protected void setUp() throws Exception { + if (LOCAL_CACHE_REUSE) { + boolean localCacheRebuild = false; + + if (LOCAL_CACHE_DIR.exists()) { + if (isLocalCacheOutdated()) { + System.out.println("-- Local caches outdated --"); + deleteLocalCacheDirectory(true); + localCacheRebuild = true; + } + } + else { + localCacheRebuild = true; + } + + overrideTempOutputDirectory(); + CUSTOM_LIBRARY_JAR = new File(LOCAL_CACHE_DIR, "debuggerCustomLibrary.jar"); + IS_TINY_APP_COMPILED = !localCacheRebuild; + } + + VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory()); + if (DexLikeBytecodePatchKt.needDexPatch(getTestName(true))) { + NoStrataPositionManagerHelperKt.setEmulateDexDebugInTests(true); + } + super.setUp(); + } + + @Override + protected void runTest() throws Throwable { + super.runTest(); + if(getDebugProcess() != null) { + getDebugProcess().getProcessHandler().startNotify(); + waitProcess(getDebugProcess().getProcessHandler()); + waitForCompleted(); + //disposeSession(myDebuggerSession); + assertNull(DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(getDebugProcess().getProcessHandler())); + myDebuggerSession = null; + } + + if (getChecker().contains("JVMTI_ERROR_WRONG_PHASE(112)")) { + myRestart.incrementAndGet(); + if (needsRestart()) { + return; + } + } else { + myRestart.set(0); + } + + throwExceptionsIfAny(); + checkTestOutput(); + } + + private boolean needsRestart() { + int restart = myRestart.get(); + return restart > 0 && restart <= 3; + } + + private static void deleteLocalCacheDirectory(boolean assertDeleteSuccess) { + System.out.println("-- Remove local cache directory --"); + boolean deleteResult = FilesKt.deleteRecursively(LOCAL_CACHE_DIR); + if (assertDeleteSuccess) { + Assert.assertTrue("Failed to delete local cache!", deleteResult); + } + } + + private static long cachedDataTimeStamp() { + File testDataLastModifiedFile = JetTestUtilsKt.findLastModifiedFile( + TINY_APP_SRC, + file -> FilesKt.getExtension(file).equals("out") || file.isDirectory() + ); + + File distLibLastModifiedFile = JetTestUtilsKt.findLastModifiedFile( + PathUtil.getKotlinPathsForDistDirectory().getLibPath(), file -> false); + + return Math.max(testDataLastModifiedFile.lastModified(), distLibLastModifiedFile.lastModified()); + } + + private static boolean isLocalCacheOutdated() { + if (!LOCAL_CACHE_LAST_MODIFIED_FILE.exists()) return true; + + String text; + try { + text = FileUtil.loadFile(LOCAL_CACHE_LAST_MODIFIED_FILE); + } + catch (IOException e) { + throw ExceptionUtilsKt.rethrow(e); + } + + long cachedFor = Long.parseLong(text); + long currentLastDate = cachedDataTimeStamp(); + + return currentLastDate != cachedFor; + } + + private static void overrideTempOutputDirectory() { + try { + Field ourOutputRootField = ExecutionTestCase.class.getDeclaredField("ourOutputRoot"); + ourOutputRootField.setAccessible(true); + + if (!LOCAL_CACHE_DIR.exists()) { + + LOCAL_CACHE_JAR_DIR.mkdirs(); + LOCAL_CACHE_APP_DIR.mkdirs(); + + boolean result = + LOCAL_CACHE_DIR.exists() && + LOCAL_CACHE_JAR_DIR.exists() && + LOCAL_CACHE_APP_DIR.exists(); + + Assert.assertTrue("Failure on local cache directories creation", result); + + boolean createFileResult = LOCAL_CACHE_LAST_MODIFIED_FILE.createNewFile(); + Assert.assertTrue("Failure on " + LOCAL_CACHE_LAST_MODIFIED_FILE.getName() + " creation", createFileResult); + + long lastModificationDate = cachedDataTimeStamp(); + FileUtil.writeToFile(LOCAL_CACHE_LAST_MODIFIED_FILE, Long.toString(lastModificationDate)); + } + + ourOutputRootField.set(null, LOCAL_CACHE_APP_DIR); + } + catch (NoSuchFieldException | IOException | IllegalAccessException e) { + throw ExceptionUtilsKt.rethrow(e); + } + } + + private static void configureLibrary( + @NotNull ModifiableRootModel model, + @NotNull String libraryName, + @NotNull File classes, + @NotNull File sources + ) { + NewLibraryEditor customLibEditor = new NewLibraryEditor(); + customLibEditor.setName(libraryName); + + customLibEditor.addRoot(VfsUtil.getUrlForLibraryRoot(classes), OrderRootType.CLASSES); + customLibEditor.addRoot(VfsUtil.getUrlForLibraryRoot(sources), OrderRootType.SOURCES); + + ConfigLibraryUtil.INSTANCE.addLibrary(customLibEditor, model, null); + } + + @Override + protected void tearDown() throws Exception { + if (DexLikeBytecodePatchKt.needDexPatch(getTestName(true))) { + NoStrataPositionManagerHelperKt.setEmulateDexDebugInTests(false); + } + + EdtTestUtil.runInEdtAndWait(() -> { + ConfigLibraryUtil.INSTANCE.removeLibrary(getModule(), CUSTOM_LIBRARY_NAME); + ConfigLibraryUtil.INSTANCE.removeLibrary(getModule(), KOTLIN_LIBRARY_NAME); + }); + + super.tearDown(); + VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory()); + } + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") + @Override + protected void setUpModule() { + super.setUpModule(); + + IdeaTestUtil.setModuleLanguageLevel(myModule, LanguageLevel.JDK_1_6); + + String outputDirPath = getAppOutputPath(); + File outDir = new File(outputDirPath); + + if (!IS_TINY_APP_COMPILED) { + try { + String modulePath = getTestAppPath(); + + //noinspection ConstantConditions + File jarDir = LOCAL_CACHE_REUSE ? LOCAL_CACHE_DIR : KotlinTestUtils.tmpDir("debuggerCustomLibrary"); + + CUSTOM_LIBRARY_JAR = MockLibraryUtil.compileLibraryToJar(CUSTOM_LIBRARY_SOURCES.getPath(), jarDir, "debuggerCustomLibrary"); + + String sourcesDir = modulePath + File.separator + "src"; + + MockLibraryUtil.compileKotlin(sourcesDir, outDir, CUSTOM_LIBRARY_JAR.getPath()); + + List options = + Arrays.asList("-d", outputDirPath, "-classpath", ForTestCompileRuntime.runtimeJarForTests().getPath(), "-g"); + KotlinTestUtils.compileJavaFiles(findJavaFiles(new File(sourcesDir)), options); + + DexLikeBytecodePatchKt.patchDexTests(outDir); + + IS_TINY_APP_COMPILED = true; + } + catch (Throwable e) { + deleteLocalCacheDirectory(false); + throw new RuntimeException(e); + } + } + + CompilerUtil.refreshOutputRoots(Lists.newArrayList(outputDirPath)); + + ApplicationManager.getApplication().runWriteAction(() -> { + ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel(); + configureLibrary(model, CUSTOM_LIBRARY_NAME, CUSTOM_LIBRARY_JAR, CUSTOM_LIBRARY_SOURCES); + configureLibrary(model, KOTLIN_LIBRARY_NAME, ForTestCompileRuntime.runtimeJarForTests(), new File("libraries/stdlib/src")); + model.commit(); + }); + + if (!outDir.exists()) { + deleteLocalCacheDirectory(false); + Assert.fail("Output directory for module wasn't created: " + outDir.getAbsolutePath()); + } + } + + private static List findJavaFiles(@NotNull File directory) { + List result = new ArrayList<>(); + if (directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + result.addAll(findJavaFiles(file)); + } + else if (file.getName().endsWith(".java")) { + result.add(file); + } + } + } + } + return result; + } + + @Override + protected JavaParameters createJavaParameters(String mainClass) { + JavaParameters parameters = super.createJavaParameters(mainClass); + parameters.getClassPath().add(ForTestCompileRuntime.runtimeJarForTests()); + parameters.getClassPath().add(CUSTOM_LIBRARY_JAR); + return parameters; + } + + @Override + protected void createBreakpoints(String className) { + PsiClass[] psiClasses = ApplicationManager.getApplication().runReadAction( + (Computable) () -> JavaPsiFacade.getInstance(myProject) + .findClasses(className, GlobalSearchScope.allScope(myProject))); + + for (PsiClass psiClass : psiClasses) { + if (psiClass instanceof KtLightClassForFacade) { + Collection files = ((KtLightClassForFacade) psiClass).getFiles(); + for (KtFile jetFile : files) { + createBreakpoints(jetFile); + } + } + else if (psiClass instanceof FakeLightClassForFileOfPackage) { + // skip, because we already create breakpoints using KotlinLightClassForPackage + } + else { + createBreakpoints(psiClass.getContainingFile()); + } + } + } + + @SuppressWarnings("MethodMayBeStatic") + protected void createDebugProcess(@NotNull String path) throws Exception { + File file = new File(path); + //noinspection ConstantConditions + FileBasedIndex.getInstance().requestReindex(VfsUtil.findFileByIoFile(file, true)); + String packageName = file.getName().replace(".kt", ""); + FqName packageFQN = new FqName(packageName); + String mainClassName = PackagePartClassUtils.getPackagePartFqName(packageFQN, file.getName()).asString(); + createLocalProcess(mainClassName); + } + + @Override + protected Sdk getTestProjectJdk() { + return PluginTestCaseBase.fullJdk(); + } + + @Override + protected void checkTestOutput() throws Exception { + if (KotlinTestUtils.isAllFilesPresentTest(getTestName(false))) { + return; + } + + try { + super.checkTestOutput(); + } + catch (ComparisonFailure e) { + KotlinTestUtils.assertEqualsToFile( + new File(this.getClass().getAnnotation(TestMetadata.class).value(), getTestName(true) + ".out"), + e.getActual()); + } + } + + @Override + public Object getData(String dataId) { + if (XDebugSession.DATA_KEY.is(dataId)) { + return myDebuggerSession == null ? null : myDebuggerSession.getXDebugSession(); + } + return super.getData(dataId); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java index dd4ca74b3f8..176336b179d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java +++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.parameterInfo; import com.intellij.lang.parameterInfo.UpdateParameterInfoContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.UserDataHolderEx; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; @@ -101,6 +102,11 @@ public class MockUpdateParameterInfoContext implements UpdateParameterInfoContex return false; } + @Override + public UserDataHolderEx getCustomContext() { + return null; + } + @Override public Project getProject() { return null; diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java.173 b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java.173 new file mode 100644 index 00000000000..dd4ca74b3f8 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java.173 @@ -0,0 +1,124 @@ +/* + * 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.parameterInfo; + +import com.intellij.lang.parameterInfo.UpdateParameterInfoContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; + +public class MockUpdateParameterInfoContext implements UpdateParameterInfoContext { + private int myCurrentParameter = -1; + private PsiFile myFile; + private JavaCodeInsightTestFixture myFixture; + + MockUpdateParameterInfoContext(PsiFile file, JavaCodeInsightTestFixture fixture) { + myFile = file; + myFixture = fixture; + } + + + @Override + public void removeHint() { + } + + @Override + public void setParameterOwner(PsiElement o) { + } + + @Override + public PsiElement getParameterOwner() { + return null; + } + + @Override + public void setHighlightedParameter(Object parameter) { + } + + @Override + public Object getHighlightedParameter() { + return null; + } + + @Override + public void setCurrentParameter(int index) { + myCurrentParameter = index; + } + + public int getCurrentParameter() { + return myCurrentParameter; + } + + @Override + public boolean isUIComponentEnabled(int index) { + return false; + } + + @Override + public void setUIComponentEnabled(int index, boolean b) { + } + + @Override + public int getParameterListStart() { + return 0; + } + + @Override + public Object[] getObjectsToView() { + return ArrayUtil.EMPTY_OBJECT_ARRAY; + } + + @Override + public boolean isPreservedOnHintHidden() { + return false; + } + + @Override + public void setPreservedOnHintHidden(boolean value) { + + } + + @Override + public boolean isInnermostContext() { + return false; + } + + @Override + public Project getProject() { + return null; + } + + @Override + public PsiFile getFile() { + return myFile; + } + + @Override + public int getOffset() { + return myFixture.getCaretOffset(); + } + + @NotNull + @Override + public Editor getEditor() { + return myFixture.getEditor(); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt index 29d91ecd36f..570c765b8ae 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt @@ -22,6 +22,7 @@ import com.intellij.lang.jvm.JvmModifier import com.intellij.lang.jvm.actions.* import com.intellij.lang.jvm.types.JvmSubstitutor import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Pair.pair import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiJvmSubstitutor import com.intellij.psi.PsiSubstitutor @@ -38,15 +39,28 @@ import org.junit.Assert class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { private class SimpleMethodRequest( - project: Project, - override val methodName: String, - override val modifiers: Collection = emptyList(), - override val returnType: ExpectedTypes = emptyList(), - override val annotations: Collection = emptyList(), - override val parameters: List = emptyList(), - override val targetSubstitutor: JvmSubstitutor = PsiJvmSubstitutor(project, PsiSubstitutor.EMPTY) + project: Project, + private val methodName: String, + private val modifiers: Collection = emptyList(), + private val returnType: ExpectedTypes = emptyList(), + private val annotations: Collection = emptyList(), + private val parameters: List>> = emptyList(), + private val targetSubstitutor: JvmSubstitutor = PsiJvmSubstitutor(project, PsiSubstitutor.EMPTY) ) : CreateMethodRequest { - override val isValid: Boolean = true + override fun getTargetSubstitutor(): JvmSubstitutor = targetSubstitutor + + override fun getModifiers() = modifiers + + override fun getMethodName() = methodName + + override fun getAnnotations() = annotations + + override fun getParameters() = parameters + + override fun getReturnType() = returnType + + override fun isValid(): Boolean = true + } private class NameInfo(vararg names: String) : SuggestedNameInfo(names) @@ -110,13 +124,80 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { """.trim(), true) } + fun testMakePrivatePublic() { + myFixture.configureByText( + "foo.kt", """class Foo { + | private fun bar(){} + |}""".trim().trimMargin() + ) + + myFixture.launchAction( + createModifierActions( + myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true) + ).findWithText("Remove 'private' modifier") + ) + myFixture.checkResult( + """class Foo { + | fun bar(){} + |}""".trim().trimMargin(), true + ) + } + + fun testMakeProtectedPublic() { + myFixture.configureByText( + "foo.kt", """open class Foo { + | protected fun bar(){} + |}""".trim().trimMargin() + ) + + myFixture.launchAction( + createModifierActions( + myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true) + ).findWithText("Remove 'protected' modifier") + ) + myFixture.checkResult( + """open class Foo { + | fun bar(){} + |}""".trim().trimMargin(), true + ) + } + + fun testMakeInternalPublic() { + myFixture.configureByText( + "foo.kt", """class Foo { + | internal fun bar(){} + |}""".trim().trimMargin() + ) + + myFixture.launchAction( + createModifierActions( + myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true) + ).findWithText("Remove 'internal' modifier") + ) + myFixture.checkResult( + """class Foo { + | fun bar(){} + |}""".trim().trimMargin(), true + ) + } + + fun testDontMakePublicPublic() { + myFixture.configureByText( + "foo.kt", """class Foo { + | fun bar(){} + |}""".trim().trimMargin() + ) + + assertEmpty(createModifierActions(myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PUBLIC, true))) + } + fun testDontMakeFunInObjectsOpen() { myFixture.configureByText("foo.kt", """ object Foo { fun bar(){} } """.trim()) - Assert.assertTrue(createModifierActions(myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.FINAL, false)).isEmpty()) + assertEmpty(createModifierActions(myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.FINAL, false))) } fun testAddVoidVoidMethod() { @@ -177,8 +258,7 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { myFixture.launchAction( createConstructorActions( - myFixture.atCaret(), - MemberRequest.Constructor(parameters = makeParams(PsiType.INT)) + myFixture.atCaret(), constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType))) ).findWithText("Add primary constructor to 'Foo'") ) myFixture.checkResult(""" @@ -195,8 +275,8 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { myFixture.launchAction( createConstructorActions( - myFixture.atCaret(), - MemberRequest.Constructor(parameters = makeParams(PsiType.INT)) + myFixture.atCaret(), + constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType))) ).findWithText("Add secondary constructor to 'Foo'") ) myFixture.checkResult(""" @@ -216,8 +296,8 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { myFixture.launchAction( createConstructorActions( - myFixture.atCaret(), - MemberRequest.Constructor(parameters = makeParams(PsiType.INT)) + myFixture.atCaret(), + constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType))) ).findWithText("Add 'int' as 1st parameter to method 'Foo'") ) myFixture.checkResult(""" @@ -234,8 +314,8 @@ class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { myFixture.launchAction( createConstructorActions( - myFixture.atCaret(), - MemberRequest.Constructor() + myFixture.atCaret(), + constructorRequest(project, listOf(pair("param0", PsiType.INT as PsiType))) ).findWithText("Remove 1st parameter from method 'Foo'") ) myFixture.checkResult(""" diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.173 new file mode 100644 index 00000000000..29d91ecd36f --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsTest.kt.173 @@ -0,0 +1,350 @@ +/* + * 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.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.lang.jvm.JvmElement +import com.intellij.lang.jvm.JvmModifier +import com.intellij.lang.jvm.actions.* +import com.intellij.lang.jvm.types.JvmSubstitutor +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiJvmSubstitutor +import com.intellij.psi.PsiSubstitutor +import com.intellij.psi.PsiType +import com.intellij.psi.codeStyle.SuggestedNameInfo +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.search.allScope +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.uast.UParameter +import org.jetbrains.uast.UastContext +import org.jetbrains.uast.toUElement +import org.junit.Assert + +class CommonIntentionActionsTest : LightPlatformCodeInsightFixtureTestCase() { + private class SimpleMethodRequest( + project: Project, + override val methodName: String, + override val modifiers: Collection = emptyList(), + override val returnType: ExpectedTypes = emptyList(), + override val annotations: Collection = emptyList(), + override val parameters: List = emptyList(), + override val targetSubstitutor: JvmSubstitutor = PsiJvmSubstitutor(project, PsiSubstitutor.EMPTY) + ) : CreateMethodRequest { + override val isValid: Boolean = true + } + + private class NameInfo(vararg names: String) : SuggestedNameInfo(names) + + override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_FULL_JDK + + fun testMakeNotFinal() { + myFixture.configureByText("foo.kt", """ + class Foo { + fun bar(){} + } + """) + + myFixture.launchAction( + createModifierActions( + myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.FINAL, false) + ).findWithText("Make 'bar' open") + ) + myFixture.checkResult(""" + class Foo { + open fun bar(){} + } + """) + } + + fun testMakePrivate() { + myFixture.configureByText("foo.kt", """ + class Foo { + fun bar(){} + } + """) + + myFixture.launchAction( + createModifierActions( + myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PRIVATE, true) + ).findWithText("Make 'Foo' private") + ) + myFixture.checkResult(""" + private class Foo { + fun bar(){} + } + """) + } + + fun testMakeNotPrivate() { + myFixture.configureByText("foo.kt", """ + private class Foo { + fun bar(){} + } + """.trim()) + + myFixture.launchAction( + createModifierActions( + myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.PRIVATE, false) + ).findWithText("Remove 'private' modifier") + ) + myFixture.checkResult(""" + class Foo { + fun bar(){} + } + """.trim(), true) + } + + fun testDontMakeFunInObjectsOpen() { + myFixture.configureByText("foo.kt", """ + object Foo { + fun bar(){} + } + """.trim()) + Assert.assertTrue(createModifierActions(myFixture.atCaret(), MemberRequest.Modifier(JvmModifier.FINAL, false)).isEmpty()) + } + + fun testAddVoidVoidMethod() { + myFixture.configureByText("foo.kt", """ + |class Foo { + | fun bar() {} + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createMethodActions( + myFixture.atCaret(), + methodRequest(project, "baz", JvmModifier.PRIVATE, PsiType.VOID) + ).findWithText("Add method 'baz' to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo { + | fun bar() {} + | private fun baz() { + | + | } + |} + """.trim().trimMargin(), true) + } + + fun testAddIntIntMethod() { + myFixture.configureByText("foo.kt", """ + |class Foo { + | fun bar() {} + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createMethodActions( + myFixture.atCaret(), + SimpleMethodRequest(project, + methodName = "baz", + modifiers = listOf(JvmModifier.PUBLIC), + returnType = expectedTypes(PsiType.INT), + parameters = expectedParams(PsiType.INT)) + ).findWithText("Add method 'baz' to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo { + | fun bar() {} + | fun baz(param0: Int): Int { + | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + | } + |} + """.trim().trimMargin(), true) + } + + fun testAddIntPrimaryConstructor() { + myFixture.configureByText("foo.kt", """ + |class Foo { + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createConstructorActions( + myFixture.atCaret(), + MemberRequest.Constructor(parameters = makeParams(PsiType.INT)) + ).findWithText("Add primary constructor to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo(param0: Int) { + |} + """.trim().trimMargin(), true) + } + + fun testAddIntSecondaryConstructor() { + myFixture.configureByText("foo.kt", """ + |class Foo() { + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createConstructorActions( + myFixture.atCaret(), + MemberRequest.Constructor(parameters = makeParams(PsiType.INT)) + ).findWithText("Add secondary constructor to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo() { + | constructor(param0: Int) { + | + | } + |} + """.trim().trimMargin(), true) + } + + fun testChangePrimaryConstructorInt() { + myFixture.configureByText("foo.kt", """ + |class Foo() { + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createConstructorActions( + myFixture.atCaret(), + MemberRequest.Constructor(parameters = makeParams(PsiType.INT)) + ).findWithText("Add 'int' as 1st parameter to method 'Foo'") + ) + myFixture.checkResult(""" + |class Foo(param0: Int) { + |} + """.trim().trimMargin(), true) + } + + fun testRemoveConstructorParameters() { + myFixture.configureByText("foo.kt", """ + |class Foo(i: Int) { + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createConstructorActions( + myFixture.atCaret(), + MemberRequest.Constructor() + ).findWithText("Remove 1st parameter from method 'Foo'") + ) + myFixture.checkResult(""" + |class Foo() { + |} + """.trim().trimMargin(), true) + } + + fun testAddStringVarProperty() { + myFixture.configureByText("foo.kt", """ + |class Foo { + | fun bar() {} + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createPropertyActions( + myFixture.atCaret(), + MemberRequest.Property( + propertyName = "baz", + visibilityModifier = JvmModifier.PUBLIC, + propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), + getterRequired = true, + setterRequired = true + ) + ).findWithText("Add 'var' property 'baz' to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo { + | var baz: String = TODO("initialize me") + | + | fun bar() {} + |} + """.trim().trimMargin(), true) + } + + fun testAddLateInitStringVarProperty() { + myFixture.configureByText("foo.kt", """ + |class Foo { + | fun bar() {} + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createPropertyActions( + myFixture.atCaret(), + MemberRequest.Property( + propertyName = "baz", + visibilityModifier = JvmModifier.PUBLIC, + propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), + getterRequired = true, + setterRequired = true + ) + ).findWithText("Add 'lateinit var' property 'baz' to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo { + | lateinit var baz: String + | + | fun bar() {} + |} + """.trim().trimMargin(), true) + } + + fun testAddStringValProperty() { + myFixture.configureByText("foo.kt", """ + |class Foo { + | fun bar() {} + |} + """.trim().trimMargin()) + + myFixture.launchAction( + createPropertyActions( + myFixture.atCaret(), + MemberRequest.Property( + propertyName = "baz", + visibilityModifier = JvmModifier.PUBLIC, + propertyType = PsiType.getTypeByName("java.lang.String", project, project.allScope()), + getterRequired = true, + setterRequired = false + ) + ).findWithText("Add 'val' property 'baz' to 'Foo'") + ) + myFixture.checkResult(""" + |class Foo { + | val baz: String = TODO("initialize me") + | + | fun bar() {} + |} + """.trim().trimMargin(), true) + } + + private fun makeParams(vararg psyTypes: PsiType): List { + val uastContext = UastContext(myFixture.project) + val factory = JavaPsiFacade.getElementFactory(myFixture.project) + val parameters = psyTypes.mapIndexed { index, psiType -> factory.createParameter("param$index", psiType) } + return parameters.map { uastContext.convertElement(it, null, UParameter::class.java) as UParameter } + } + + private fun expectedTypes(vararg psiTypes: PsiType) = psiTypes.map { expectedType(it) } + + private fun expectedParams(vararg psyTypes: PsiType) = + psyTypes.mapIndexed { index, psiType -> NameInfo("param$index") to expectedTypes(psiType) } + + private inline fun CodeInsightTestFixture.atCaret() = elementAtCaret.toUElement() as T + + @Suppress("CAST_NEVER_SUCCEEDS") + private fun List.findWithText(text: String): IntentionAction = + this.firstOrNull { it.text == text } ?: + Assert.fail("intention with text '$text' was not found, only ${this.joinToString { "\"${it.text}\"" }} available") as Nothing +} + diff --git a/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt index 5a5b6908e16..4d9498a72a7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt @@ -159,7 +159,7 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() { val runConfiguration = createConfigurationFromObject("renameTest.Foo", save = true) - val pkg = JavaPsiFacade.getInstance(getTestProject()).findPackage("renameTest") + val pkg = JavaPsiFacade.getInstance(getTestProject()).findPackage("renameTest")!! val rename = RefactoringFactory.getInstance(getTestProject()).createRename(pkg, "afterRenameTest") rename.run() diff --git a/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt.173 b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt.173 new file mode 100644 index 00000000000..5a5b6908e16 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/run/RunConfigurationTest.kt.173 @@ -0,0 +1,278 @@ +/* + * 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.run + +import com.intellij.execution.Location +import com.intellij.execution.PsiLocation +import com.intellij.execution.actions.ConfigurationContext +import com.intellij.openapi.module.Module +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiManager +import com.intellij.refactoring.RefactoringFactory +import com.intellij.testFramework.MapDataContext +import com.intellij.testFramework.PlatformTestCase +import com.intellij.testFramework.PsiTestUtil +import org.jetbrains.kotlin.idea.MainFunctionDetector +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.search.allScope +import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex +import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil +import org.jetbrains.kotlin.idea.test.KotlinCodeInsightTestCase +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.* +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.allChildren +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.junit.Assert +import java.io.File +import java.util.* + +private val RUN_PREFIX = "// RUN:" + +class RunConfigurationTest: KotlinCodeInsightTestCase() { + fun getTestProject() = myProject!! + override fun getModule() = myModule!! + + fun testMainInTest() { + val createResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createResult.module, addJdk(testRootDisposable, ::mockJdk)) + + val runConfiguration = createConfigurationFromMain("some.main") + val javaParameters = getJavaRunParameters(runConfiguration) + + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(createResult.srcOutputDir)) + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(createResult.testOutputDir)) + + fun functionVisitor(function: KtNamedFunction) { + val options = function.bodyExpression?.allChildren?.filterIsInstance()?.map { it.text.trim().replace("//", "").trim() }?.filter { it.isNotBlank() }?.toList() ?: emptyList() + if (options.isNotEmpty()) { + val assertIsMain = "yes" in options + val assertIsNotMain = "no" in options + + val bindingContext = function.analyze(BodyResolveMode.FULL) + val isMainFunction = MainFunctionDetector(bindingContext).isMain(function) + + if (assertIsMain) { + Assert.assertTrue("The function ${function.fqName?.asString()} should be main", isMainFunction) + } + if (assertIsNotMain) { + Assert.assertFalse("The function ${function.fqName?.asString()} should NOT be main", isMainFunction) + } + + if (isMainFunction) { + createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration() + + Assert.assertNotNull("Kotlin configuration producer should produce configuration for ${function.fqName?.asString()}", + KotlinRunConfigurationProducer.getEntryPointContainer(function)) + } else { + try { + createConfigurationFromMain(function.fqName?.asString()!!).checkConfiguration() + Assert.fail("configuration for function ${function.fqName?.asString()} at least shouldn't pass checkConfiguration()") + } catch (expected: Throwable) { + } + + Assert.assertNull("Kotlin configuration producer shouldN'T produce configuration for ${function.fqName?.asString()}", + KotlinRunConfigurationProducer.getEntryPointContainer(function)) + } + } + } + + createResult.srcDir.children.filter { it.extension == "kt" }.forEach { + val psiFile = PsiManager.getInstance(createResult.module.project).findFile(it) + if (psiFile is KtFile) { + psiFile.acceptChildren(object : KtVisitorVoid() { + override fun visitNamedFunction(function: KtNamedFunction) { + functionVisitor(function) + } + }) + } + } + } + + fun testDependencyModuleClasspath() { + val dependencyModuleSrcDir = configureModule(moduleDirPath("module"), getTestProject().baseDir!!).srcOutputDir + + val moduleWithDependencyDir = runWriteAction { getTestProject().baseDir!!.createChildDirectory(this, "moduleWithDependency") } + + val moduleWithDependency = createModule("moduleWithDependency") + ModuleRootModificationUtil.setModuleSdk(moduleWithDependency, testProjectJdk) + + val moduleWithDependencySrcDir = configureModule( + moduleDirPath("moduleWithDependency"), moduleWithDependencyDir, configModule = moduleWithDependency).srcOutputDir + + ModuleRootModificationUtil.addDependency(moduleWithDependency, module) + + val kotlinRunConfiguration = createConfigurationFromMain("some.test.main") + kotlinRunConfiguration.setModule(moduleWithDependency) + + val javaParameters = getJavaRunParameters(kotlinRunConfiguration) + + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(dependencyModuleSrcDir)) + Assert.assertTrue(javaParameters.classPath.rootDirs.contains(moduleWithDependencySrcDir)) + } + + fun testClassesAndObjects() { + doTest(ConfigLibraryUtil::configureKotlinRuntimeAndSdk) + } + + fun testInJsModule() { + doTest(ConfigLibraryUtil::configureKotlinJsRuntimeAndSdk) + } + + fun testUpdateOnClassRename() { + val createModuleResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk)) + + val runConfiguration = createConfigurationFromObject("renameTest.Foo", save = true) + + val obj = KotlinFullClassNameIndex.getInstance().get("renameTest.Foo", getTestProject(), getTestProject().allScope()).single() + val rename = RefactoringFactory.getInstance(getTestProject()).createRename(obj, "Bar") + rename.run() + + Assert.assertEquals("renameTest.Bar", runConfiguration.MAIN_CLASS_NAME) + } + + fun testUpdateOnPackageRename() { + val createModuleResult = configureModule(moduleDirPath("module"), getTestProject().baseDir!!) + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk)) + + val runConfiguration = createConfigurationFromObject("renameTest.Foo", save = true) + + val pkg = JavaPsiFacade.getInstance(getTestProject()).findPackage("renameTest") + val rename = RefactoringFactory.getInstance(getTestProject()).createRename(pkg, "afterRenameTest") + rename.run() + + Assert.assertEquals("afterRenameTest.Foo", runConfiguration.MAIN_CLASS_NAME) + } + + fun testWithModuleForJdk6() { + checkModuleInfoName(null, addJdk(testRootDisposable, ::mockJdk)) + } + + fun testWithModuleForJdk9() { + checkModuleInfoName("MAIN", addJdk(testRootDisposable, ::mockJdk9)) + } + + fun testWithModuleForJdk9WithoutModuleInfo() { + checkModuleInfoName(null, addJdk(testRootDisposable, ::mockJdk9)) + } + + private fun checkModuleInfoName(moduleName: String?, sdk: Sdk) { + val module = configureModule(moduleDirPath("module"), getTestProject().baseDir!!).module + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, sdk) + + val javaParameters = getJavaRunParameters(createConfigurationFromMain("some.main")) + + Assert.assertEquals(moduleName, javaParameters.moduleName) + } + + private fun doTest(configureRuntime: (Module, Sdk) -> Unit) { + val baseDir = getTestProject().baseDir!! + val createModuleResult = configureModule(moduleDirPath("module"), baseDir) + val srcDir = createModuleResult.srcDir + + configureRuntime(createModuleResult.module, addJdk(testRootDisposable, ::mockJdk)) + + try { + val expectedClasses = ArrayList() + val actualClasses = ArrayList() + + val testFile = PsiManager.getInstance(getTestProject()).findFile(srcDir.findFileByRelativePath("test.kt")!!)!! + testFile.accept( + object : KtTreeVisitorVoid() { + override fun visitComment(comment: PsiComment) { + val declaration = comment.getStrictParentOfType()!! + val text = comment.text ?: return + if (!text.startsWith(RUN_PREFIX)) return + + val expectedClass = text.substring(RUN_PREFIX.length).trim() + if (expectedClass.isNotEmpty()) expectedClasses.add(expectedClass) + + val dataContext = MapDataContext() + dataContext.put(Location.DATA_KEY, PsiLocation(getTestProject(), declaration)) + val context = ConfigurationContext.getFromContext(dataContext) + val actualClass = (context.configuration?.configuration as? KotlinRunConfiguration)?.runClass + if (actualClass != null) { + actualClasses.add(actualClass) + } + } + } + ) + Assert.assertEquals(expectedClasses, actualClasses) + } + finally { + ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(createModuleResult.module, mockJdk()) + } + } + + private fun createConfigurationFromMain(mainFqn: String): KotlinRunConfiguration { + val mainFunction = KotlinTopLevelFunctionFqnNameIndex.getInstance().get(mainFqn, getTestProject(), getTestProject().allScope()).first() + + return createConfigurationFromElement(mainFunction) as KotlinRunConfiguration + } + + private fun createConfigurationFromObject(objectFqn: String, save: Boolean = false): KotlinRunConfiguration { + val obj = KotlinFullClassNameIndex.getInstance().get(objectFqn, getTestProject(), getTestProject().allScope()).single() + val mainFunction = obj.declarations.single { it is KtFunction && it.getName() == "main" } + return createConfigurationFromElement(mainFunction, save) as KotlinRunConfiguration + } + + private fun configureModule(moduleDir: String, outputParentDir: VirtualFile, configModule: Module = module): CreateModuleResult { + val srcPath = moduleDir + "/src" + val srcDir = PsiTestUtil.createTestProjectStructure(project, configModule, srcPath, PlatformTestCase.myFilesToDelete, true) + + val testPath = moduleDir + "/test" + if (File(testPath).exists()) { + val testDir = PsiTestUtil.createTestProjectStructure(project, configModule, testPath, PlatformTestCase.myFilesToDelete, false) + PsiTestUtil.addSourceRoot(module, testDir, true) + } + + val (srcOutDir, testOutDir) = runWriteAction { + val outDir = outputParentDir.createChildDirectory(this, "out") + val srcOutDir = outDir.createChildDirectory(this, "production") + val testOutDir = outDir.createChildDirectory(this, "test") + + PsiTestUtil.setCompilerOutputPath(configModule, srcOutDir.url, false) + PsiTestUtil.setCompilerOutputPath(configModule, testOutDir.url, true) + + Pair(srcOutDir, testOutDir) + } + + PsiDocumentManager.getInstance(getTestProject()).commitAllDocuments() + + return CreateModuleResult(configModule, srcDir, srcOutDir, testOutDir) + } + + private fun moduleDirPath(moduleName: String) = "${testDataPath}${getTestName(false)}/$moduleName" + + override fun getTestDataPath() = getTestDataPathBase() + "/run/" + override fun getTestProjectJdk() = mockJdk() + + private class CreateModuleResult( + val module: Module, + val srcDir: VirtualFile, + val srcOutputDir: VirtualFile, + val testOutputDir: VirtualFile + ) +} diff --git a/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt b/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt index b7ad1e09644..a4b2cfc6a27 100644 --- a/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt @@ -16,8 +16,8 @@ package org.jetbrains.kotlin.psi -import com.intellij.injected.editor.DocumentWindowImpl import com.intellij.injected.editor.EditorWindow +import com.intellij.lang.injection.InjectedLanguageManager import com.intellij.openapi.util.TextRange import com.intellij.psi.injection.Injectable import com.intellij.testFramework.LightProjectDescriptor @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCaseBase import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.utils.SmartList abstract class AbstractInjectionTest : KotlinLightCodeInsightFixtureTestCase() { override fun getProjectDescriptor(): LightProjectDescriptor { @@ -62,8 +63,13 @@ abstract class AbstractInjectionTest : KotlinLightCodeInsightFixtureTestCase() { assertInjectionPresent(languageId, unInjectShouldBePresent) if (shreds != null) { - val actualShreds = (editor.document as DocumentWindowImpl).shreds.map { - ShredInfo(it.range, it.rangeInsideHost, it.prefix, it.suffix) + val actualShreds = SmartList().apply { + val host = InjectedLanguageManager.getInstance(project).getInjectionHost(file.viewProvider) + InjectedLanguageManager.getInstance(project).enumerate(host, { _, placesInFile -> + addAll(placesInFile.map { + ShredInfo(it.range, it.rangeInsideHost, it.prefix, it.suffix) + }) + }) } assertOrderedEquals( diff --git a/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt.173 b/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt.173 new file mode 100644 index 00000000000..b7ad1e09644 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/psi/AbstractInjectionTest.kt.173 @@ -0,0 +1,128 @@ +/* + * Copyright 2010-2016 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.psi + +import com.intellij.injected.editor.DocumentWindowImpl +import com.intellij.injected.editor.EditorWindow +import com.intellij.openapi.util.TextRange +import com.intellij.psi.injection.Injectable +import com.intellij.testFramework.LightProjectDescriptor +import junit.framework.TestCase +import org.intellij.lang.annotations.Language +import org.intellij.plugins.intelliLang.Configuration +import org.intellij.plugins.intelliLang.inject.InjectLanguageAction +import org.intellij.plugins.intelliLang.inject.UnInjectLanguageAction +import org.intellij.plugins.intelliLang.references.FileReferenceInjector +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCaseBase +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor + +abstract class AbstractInjectionTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): LightProjectDescriptor { + val testName = getTestName(true) + return when { + testName.endsWith("WithAnnotation") -> KotlinLightProjectDescriptor.INSTANCE + testName.endsWith("WithRuntime") -> KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + else -> KotlinLightCodeInsightFixtureTestCaseBase.JAVA_LATEST + } + } + + data class ShredInfo( + val range: TextRange, + val hostRange: TextRange, + val prefix: String = "", + val suffix: String = "") { + } + + protected fun doInjectionPresentTest( + @Language("kotlin") text: String, @Language("Java") javaText: String? = null, + languageId: String? = null, unInjectShouldBePresent: Boolean = true, + shreds: List? = null) { + if (javaText != null) { + myFixture.configureByText("${getTestName(true)}.java", javaText.trimIndent()) + } + + myFixture.configureByText("${getTestName(true)}.kt", text.trimIndent()) + + assertInjectionPresent(languageId, unInjectShouldBePresent) + + if (shreds != null) { + val actualShreds = (editor.document as DocumentWindowImpl).shreds.map { + ShredInfo(it.range, it.rangeInsideHost, it.prefix, it.suffix) + } + + assertOrderedEquals( + actualShreds.sortedBy { it.range.startOffset }, + shreds.sortedBy { it.range.startOffset }) + } + } + + protected fun assertInjectionPresent(languageId: String?, unInjectShouldBePresent: Boolean) { + TestCase.assertFalse("Injection action is available. There's probably no injection at caret place", + InjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file)) + + if (languageId != null) { + val injectedFile = (editor as? EditorWindow)?.injectedFile + assertEquals("Wrong injection language", languageId, injectedFile?.language?.id) + } + + if (unInjectShouldBePresent) { + TestCase.assertTrue("UnInjection action is not available. There's no injection at caret place or some other troubles.", + UnInjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file)) + } + } + + protected fun assertNoInjection(@Language("kotlin") text: String) { + myFixture.configureByText("${getTestName(true)}.kt", text.trimIndent()) + + TestCase.assertTrue("Injection action is not available. There's probably some injection but nothing was expected.", + InjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file)) + } + + protected fun doRemoveInjectionTest(@Language("kotlin") before: String, @Language("kotlin") after: String) { + myFixture.setCaresAboutInjection(false) + + myFixture.configureByText("${getTestName(true)}.kt", before.trimIndent()) + + TestCase.assertTrue(UnInjectLanguageAction().isAvailable(project, myFixture.editor, myFixture.file)) + UnInjectLanguageAction.invokeImpl(project, myFixture.editor, myFixture.file) + + myFixture.checkResult(after.trimIndent()) + } + + protected fun doFileReferenceInjectTest(@Language("kotlin") before: String, @Language("kotlin") after: String) { + doTest(FileReferenceInjector(), before, after) + } + + protected fun doTest(injectable: Injectable, @Language("kotlin") before: String, @Language("kotlin") after: String) { + val configuration = Configuration.getProjectInstance(project).advancedConfiguration + val allowed = configuration.isSourceModificationAllowed + + configuration.isSourceModificationAllowed = true + try { + myFixture.configureByText("${getTestName(true)}.kt", before.trimIndent()) + InjectLanguageAction.invokeImpl(project, myFixture.editor, myFixture.file, injectable) + myFixture.checkResult(after.trimIndent()) + } + finally { + configuration.isSourceModificationAllowed = allowed + } + } + + fun range(start: Int, end: Int) = TextRange.create(start, end) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt b/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt index 59fd1fc77a8..87b1aa754ed 100644 --- a/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt +++ b/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt @@ -64,7 +64,7 @@ class StringTemplateExpressionManipulatorTest : KotlinLightCodeInsightFixtureTes val expression = KtPsiFactory(project).createExpression(original) as KtStringTemplateExpression val manipulator = ElementManipulators.getNotNullManipulator(expression) val newExpression = if (range == null) manipulator.handleContentChange(expression, newContent) else manipulator.handleContentChange(expression, range, newContent) - assertEquals(expected, newExpression.text) + assertEquals(expected, newExpression?.text) } override fun getProjectDescriptor() = KotlinLightProjectDescriptor.INSTANCE diff --git a/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt.173 b/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt.173 new file mode 100644 index 00000000000..59fd1fc77a8 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/psi/StringTemplateExpressionManipulatorTest.kt.173 @@ -0,0 +1,71 @@ +/* + * 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.psi + +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor +import com.intellij.psi.ElementManipulators +import org.junit.Assert.* +import com.intellij.openapi.util.TextRange + +class StringTemplateExpressionManipulatorTest : KotlinLightCodeInsightFixtureTestCase() { + fun testSingleQuoted() { + doTestContentChange("\"a\"", "b", "\"b\"") + doTestContentChange("\"\"", "b", "\"b\"") + doTestContentChange("\"a\"", "\t", "\"\\t\"") + doTestContentChange("\"a\"", "\n", "\"\\n\"") + doTestContentChange("\"a\"", "\\t", "\"\\\\t\"") + } + + fun testUnclosedQuoted() { + doTestContentChange("\"a", "b", "\"b") + doTestContentChange("\"", "b", "\"b") + doTestContentChange("\"a", "\t", "\"\\t") + doTestContentChange("\"a", "\n", "\"\\n") + doTestContentChange("\"a", "\\t", "\"\\\\t") + } + + fun testTripleQuoted() { + doTestContentChange("\"\"\"a\"\"\"", "b", "\"\"\"b\"\"\"") + doTestContentChange("\"\"\"\"\"\"", "b", "\"\"\"b\"\"\"") + doTestContentChange("\"\"\"a\"\"\"", "\t", "\"\"\"\t\"\"\"") + doTestContentChange("\"\"\"a\"\"\"", "\n", "\"\"\"\n\"\"\"") + doTestContentChange("\"\"\"a\"\"\"", "\\t", "\"\"\"\\t\"\"\"") + } + + fun testUnclosedTripleQuoted() { + doTestContentChange("\"\"\"a", "b", "\"\"\"b") + doTestContentChange("\"\"\"", "b", "\"\"\"b") + doTestContentChange("\"\"\"a", "\t", "\"\"\"\t") + doTestContentChange("\"\"\"a", "\n", "\"\"\"\n") + doTestContentChange("\"\"\"a", "\\t", "\"\"\"\\t") + } + + fun testReplaceRange() { + doTestContentChange("\"abc\"", "x", range = TextRange(2,3), expected = "\"axc\"") + doTestContentChange("\"\"\"abc\"\"\"", "x", range = TextRange(4,5), expected = "\"\"\"axc\"\"\"") + } + + private fun doTestContentChange(original: String, newContent: String, expected: String, range: TextRange? = null) { + val expression = KtPsiFactory(project).createExpression(original) as KtStringTemplateExpression + val manipulator = ElementManipulators.getNotNullManipulator(expression) + val newExpression = if (range == null) manipulator.handleContentChange(expression, newContent) else manipulator.handleContentChange(expression, range, newContent) + assertEquals(expected, newExpression.text) + } + + override fun getProjectDescriptor() = KotlinLightProjectDescriptor.INSTANCE +} diff --git a/include/kotlin-compiler/build.gradle.kts b/include/kotlin-compiler/build.gradle.kts index bf2853e497e..23db7d5dceb 100644 --- a/include/kotlin-compiler/build.gradle.kts +++ b/include/kotlin-compiler/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { fatJarContents(intellijDep()) { includeIntellijCoreJarDependencies(project, { !(it.startsWith("jdom") || it.startsWith("log4j")) }) } fatJarContents(intellijDep()) { includeJars("jna-platform") } fatJarContentsStripServices(intellijDep("jps-standalone")) { includeJars("jps-model") } - fatJarContentsStripMetadata(intellijDep()) { includeJars("oromatcher", "jdom", "log4j") } + fatJarContentsStripMetadata(intellijDep()) { includeJars("oro", "jdom", "log4j", rootProject = rootProject) } } val jar: Jar by tasks diff --git a/include/kotlin-compiler/build.gradle.kts.173 b/include/kotlin-compiler/build.gradle.kts.173 new file mode 100644 index 00000000000..bf2853e497e --- /dev/null +++ b/include/kotlin-compiler/build.gradle.kts.173 @@ -0,0 +1,45 @@ + +plugins { + kotlin("jvm") +} + +val compile by configurations +val fatJarContents by configurations.creating +val fatJarContentsStripMetadata by configurations.creating +val fatJarContentsStripServices by configurations.creating + +val compilerModules: Array by rootProject.extra +val compilerManifestClassPath = "kotlin-stdlib.jar kotlin-reflect.jar kotlin-script-runtime.jar" + +dependencies { + compilerModules.forEach { module -> + compile(project(module)) { isTransitive = false } + } + + fatJarContents(project(":core:builtins", configuration = "builtins")) + fatJarContents(commonDep("javax.inject")) + fatJarContents(commonDep("org.jline", "jline")) + fatJarContents(commonDep("org.fusesource.jansi", "jansi")) + fatJarContents(protobufFull()) + fatJarContents(commonDep("com.google.code.findbugs", "jsr305")) + fatJarContents(commonDep("io.javaslang", "javaslang")) + fatJarContents(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } + + fatJarContents(intellijCoreDep()) { includeJars("intellij-core") } + fatJarContents(intellijDep()) { includeIntellijCoreJarDependencies(project, { !(it.startsWith("jdom") || it.startsWith("log4j")) }) } + fatJarContents(intellijDep()) { includeJars("jna-platform") } + fatJarContentsStripServices(intellijDep("jps-standalone")) { includeJars("jps-model") } + fatJarContentsStripMetadata(intellijDep()) { includeJars("oromatcher", "jdom", "log4j") } +} + +val jar: Jar by tasks +jar.apply { + dependsOn(fatJarContents) + from(compile.filter { it.extension == "jar" }.map { zipTree(it) }) + from(fatJarContents.map { zipTree(it) }) + from(fatJarContentsStripServices.map { zipTree(it) }) { exclude("META-INF/services/**") } + from(fatJarContentsStripMetadata.map { zipTree(it) }) { exclude("META-INF/jb/** META-INF/LICENSE") } + + manifest.attributes["Class-Path"] = compilerManifestClassPath + manifest.attributes["Main-Class"] = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler" +} \ No newline at end of file diff --git a/j2k/build.gradle.kts b/j2k/build.gradle.kts index 542e7a64a58..70bb77b3aa8 100644 --- a/j2k/build.gradle.kts +++ b/j2k/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { 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")) @@ -33,6 +34,7 @@ dependencies { testRuntime(intellijPluginDep("coverage")) testRuntime(intellijPluginDep("maven")) testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) testRuntime(intellijPluginDep("junit")) testRuntime(intellijPluginDep("testng")) testRuntime(intellijPluginDep("IntelliLang")) diff --git a/j2k/build.gradle.kts.173 b/j2k/build.gradle.kts.173 new file mode 100644 index 00000000000..542e7a64a58 --- /dev/null +++ b/j2k/build.gradle.kts.173 @@ -0,0 +1,73 @@ + +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")) + + 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("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 b/jps-plugin/build.gradle.kts index 01d8ffabb1d..c1b73d11bfc 100644 --- a/jps-plugin/build.gradle.kts +++ b/jps-plugin/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { compile(projectRuntimeJar(":kotlin-preloader")) compile(project(":idea:idea-jps-common")) compileOnly(group = "org.jetbrains", name = "annotations", version = "13.0") - compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "util", "asm-all") } + 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")) @@ -26,7 +26,7 @@ dependencies { 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", "log4j") } + testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "platform-api", "log4j") } testCompile(intellijDep("jps-build-test")) compilerModules.forEach { testRuntime(project(it)) diff --git a/jps-plugin/build.gradle.kts.173 b/jps-plugin/build.gradle.kts.173 new file mode 100644 index 00000000000..01d8ffabb1d --- /dev/null +++ b/jps-plugin/build.gradle.kts.173 @@ -0,0 +1,52 @@ +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(group = "org.jetbrains", name = "annotations", version = "13.0") + compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "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", "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/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt index 435a13ace89..9af8ba59fd8 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt @@ -46,6 +46,7 @@ import org.jetbrains.jps.util.JpsPathUtil import org.jetbrains.kotlin.config.IncrementalCompilation import org.jetbrains.kotlin.incremental.CacheVersion import org.jetbrains.kotlin.incremental.LookupSymbol +import org.jetbrains.kotlin.incremental.isJavaFile import org.jetbrains.kotlin.incremental.testingUtils.* import org.jetbrains.kotlin.jps.incremental.getKotlinCache import org.jetbrains.kotlin.jps.incremental.withLookupStorage @@ -132,8 +133,9 @@ abstract class AbstractIncrementalJpsTest( // JPS forces rebuild of all files when JVM constant has been changed and Callbacks.ConstantAffectionResolver // is not provided, so ConstantAffectionResolver is mocked with empty implementation - protected open val mockConstantSearch: Callbacks.ConstantAffectionResolver? - get() = MockConstantSearch(workDir) + // Usages in Kotlin files are expected to be found by KotlinLookupConstantSearch + private val mockConstantSearch: Callbacks.ConstantAffectionResolver? + get() = MockJavaConstantSearch(workDir) private fun build(scope: CompileScopeTestBuilder = CompileScopeTestBuilder.make().allModules()): MakeResult { val workDirPath = FileUtil.toSystemIndependentName(workDir.absolutePath) @@ -493,7 +495,16 @@ abstract class AbstractIncrementalJpsTest( } } -private class MockConstantSearch(private val workDir: File) : Callbacks.ConstantAffectionResolver { +/** + * Mocks Intellij Java constant search. + * When JPS is run from Intellij, it sends find usages request to IDE (it only searches for references inside Java files). + * + * We rely on heuristics instead of precise usages search. + * A Java file is considered affected if: + * 1. It contains changed field name as a content substring. + * 2. Its simple file name is not equal to a field's owner class simple name (to avoid recompiling field's declaration again) + */ +private class MockJavaConstantSearch(private val workDir: File) : Callbacks.ConstantAffectionResolver { override fun request( ownerClassName: String, fieldName: String, @@ -501,13 +512,19 @@ private class MockConstantSearch(private val workDir: File) : Callbacks.Constant fieldRemoved: Boolean, accessChanged: Boolean ): Future { - val affectedFiles = workDir.walk().filter { it.isFile && it.isNameUsage() } - return FixedFuture(Callbacks.ConstantAffection(affectedFiles.toList())) - } + fun File.isAffected(): Boolean { + if (!isJavaFile()) return false - private fun File.isNameUsage(): Boolean = - name.equals("usage.kt", ignoreCase = true) - || name.equals("usage.java", ignoreCase = true) + if (nameWithoutExtension == ownerClassName.substringAfterLast(".")) return false + + val code = readText() + return code.contains(fieldName) + } + + + val affectedJavaFiles = workDir.walk().filter(File::isAffected).toList() + return FixedFuture(Callbacks.ConstantAffection(affectedJavaFiles)) + } } internal val ProjectDescriptor.allModuleTargets: Collection diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt.173 b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt.173 new file mode 100644 index 00000000000..435a13ace89 --- /dev/null +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/AbstractIncrementalJpsTest.kt.173 @@ -0,0 +1,521 @@ +/* + * Copyright 2010-2016 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.jps.build + +import com.intellij.openapi.Disposable +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.testFramework.TestLoggerFactory +import com.intellij.testFramework.UsefulTestCase +import com.intellij.util.concurrency.FixedFuture +import junit.framework.TestCase +import org.apache.log4j.ConsoleAppender +import org.apache.log4j.Level +import org.apache.log4j.Logger +import org.apache.log4j.PatternLayout +import org.jetbrains.jps.ModuleChunk +import org.jetbrains.jps.api.CanceledStatus +import org.jetbrains.jps.builders.BuildResult +import org.jetbrains.jps.builders.CompileScopeTestBuilder +import org.jetbrains.jps.builders.impl.BuildDataPathsImpl +import org.jetbrains.jps.builders.impl.logging.ProjectBuilderLoggerBase +import org.jetbrains.jps.builders.java.dependencyView.Callbacks +import org.jetbrains.jps.builders.logging.BuildLoggingManager +import org.jetbrains.jps.cmdline.ProjectDescriptor +import org.jetbrains.jps.incremental.* +import org.jetbrains.jps.incremental.messages.BuildMessage +import org.jetbrains.jps.model.JpsModuleRootModificationUtil +import org.jetbrains.jps.model.java.JpsJavaDependencyScope +import org.jetbrains.jps.model.java.JpsJavaExtensionService +import org.jetbrains.jps.util.JpsPathUtil +import org.jetbrains.kotlin.config.IncrementalCompilation +import org.jetbrains.kotlin.incremental.CacheVersion +import org.jetbrains.kotlin.incremental.LookupSymbol +import org.jetbrains.kotlin.incremental.testingUtils.* +import org.jetbrains.kotlin.jps.incremental.getKotlinCache +import org.jetbrains.kotlin.jps.incremental.withLookupStorage +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.utils.Printer +import org.jetbrains.kotlin.utils.keysToMap +import java.io.* +import java.util.* +import java.util.concurrent.Future +import kotlin.reflect.jvm.javaField + +abstract class AbstractIncrementalJpsTest( + private val allowNoFilesWithSuffixInTestData: Boolean = false, + private val checkDumpsCaseInsensitively: Boolean = false, + private val allowNoBuildLogFileInTestData: Boolean = false +) : BaseKotlinJpsBuildTestCase() { + companion object { + private val COMPILATION_FAILED = "COMPILATION FAILED" + + // change to "/tmp" or anything when default is too long (for easier debugging) + private val TEMP_DIRECTORY_TO_USE = File(FileUtilRt.getTempDirectory()) + + private val DEBUG_LOGGING_ENABLED = System.getProperty("debug.logging.enabled") == "true" + } + + protected lateinit var testDataDir: File + protected lateinit var workDir: File + protected lateinit var projectDescriptor: ProjectDescriptor + // is used to compare lookup dumps in a human readable way (lookup symbols are hashed in an actual lookup storage) + protected lateinit var lookupsDuringTest: MutableSet + private var isICEnabledBackup: Boolean = false + + protected var mapWorkingToOriginalFile: MutableMap = hashMapOf() + + protected open val buildLogFinder: BuildLogFinder + get() = BuildLogFinder() + + private fun enableDebugLogging() { + com.intellij.openapi.diagnostic.Logger.setFactory(TestLoggerFactory::class.java) + TestLoggerFactory.dumpLogToStdout("") + TestLoggerFactory.enableDebugLogging(testRootDisposable, "#org") + + val console = ConsoleAppender() + console.layout = PatternLayout("%d [%p|%c|%C{1}] %m%n") + console.threshold = Level.ALL + console.activateOptions() + Logger.getRootLogger().addAppender(console) + } + + private var systemPropertiesBackup = run { + val props = System.getProperties() + val output = ByteArrayOutputStream() + props.store(output, "System properties backup") + output.toByteArray() + } + + private fun restoreSystemProperties() { + val input = ByteArrayInputStream(systemPropertiesBackup) + val props = Properties() + props.load(input) + System.setProperties(props) + } + + override fun setUp() { + super.setUp() + lookupsDuringTest = hashSetOf() + isICEnabledBackup = IncrementalCompilation.isEnabled() + IncrementalCompilation.setIsEnabled(true) + + if (DEBUG_LOGGING_ENABLED) { + enableDebugLogging() + } + } + + override fun tearDown() { + restoreSystemProperties() + (AbstractIncrementalJpsTest::myProject).javaField!![this] = null + (AbstractIncrementalJpsTest::projectDescriptor).javaField!![this] = null + (AbstractIncrementalJpsTest::systemPropertiesBackup).javaField!![this] = null + lookupsDuringTest.clear() + IncrementalCompilation.setIsEnabled(isICEnabledBackup) + super.tearDown() + } + + // JPS forces rebuild of all files when JVM constant has been changed and Callbacks.ConstantAffectionResolver + // is not provided, so ConstantAffectionResolver is mocked with empty implementation + protected open val mockConstantSearch: Callbacks.ConstantAffectionResolver? + get() = MockConstantSearch(workDir) + + private fun build(scope: CompileScopeTestBuilder = CompileScopeTestBuilder.make().allModules()): MakeResult { + val workDirPath = FileUtil.toSystemIndependentName(workDir.absolutePath) + + val logger = MyLogger(workDirPath) + projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger)) + + val lookupTracker = TestLookupTracker() + projectDescriptor.project.setTestingContext(TestingContext(lookupTracker, logger)) + + try { + val builder = IncProjectBuilder(projectDescriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, mockConstantSearch, true) + val buildResult = BuildResult() + builder.addMessageHandler(buildResult) + builder.build(scope.build(), false) + + lookupTracker.lookups.mapTo(lookupsDuringTest) { LookupSymbol(it.name, it.scopeFqName) } + + if (!buildResult.isSuccessful) { + val errorMessages = + buildResult + .getMessages(BuildMessage.Kind.ERROR) + .map { it.messageText } + .map { it.replace("^.+:\\d+:\\s+".toRegex(), "").trim() } + .joinToString("\n") + return MakeResult(logger.log + "$COMPILATION_FAILED\n" + errorMessages + "\n", true, null) + } + else { + return MakeResult(logger.log, false, createMappingsDump(projectDescriptor)) + } + } + finally { + projectDescriptor.dataManager.flush(false) + projectDescriptor.release() + } + } + + private fun initialMake(): MakeResult { + val makeResult = build() + + val initBuildLogFile = File(testDataDir, "init-build.log") + if (initBuildLogFile.exists()) { + UsefulTestCase.assertSameLinesWithFile(initBuildLogFile.absolutePath, makeResult.log) + } + else { + assertFalse("Initial make failed:\n$makeResult", makeResult.makeFailed) + } + + return makeResult + } + + private fun make(): MakeResult { + return build() + } + + private fun rebuild(): MakeResult { + return build(CompileScopeTestBuilder.rebuild().allModules()) + } + + private fun rebuildAndCheckOutput(makeOverallResult: MakeResult) { + val outDir = File(getAbsolutePath("out")) + val outAfterMake = File(getAbsolutePath("out-after-make")) + + if (outDir.exists()) { + FileUtil.copyDir(outDir, outAfterMake) + } + + val rebuildResult = rebuild() + assertEquals("Rebuild failed: ${rebuildResult.makeFailed}, last make failed: ${makeOverallResult.makeFailed}. Rebuild result: $rebuildResult", + rebuildResult.makeFailed, makeOverallResult.makeFailed) + + if (!outAfterMake.exists()) { + assertFalse(outDir.exists()) + } + else { + assertEqualDirectories(outDir, outAfterMake, makeOverallResult.makeFailed) + } + + if (!makeOverallResult.makeFailed) { + if (checkDumpsCaseInsensitively && rebuildResult.mappingsDump?.toLowerCase() == makeOverallResult.mappingsDump?.toLowerCase()) { + // do nothing + } + else { + TestCase.assertEquals(rebuildResult.mappingsDump, makeOverallResult.mappingsDump) + } + } + + FileUtil.delete(outAfterMake) + } + + private fun clearCachesRebuildAndCheckOutput(makeOverallResult: MakeResult) { + FileUtil.delete(BuildDataPathsImpl(myDataStorageRoot).dataStorageRoot!!) + + rebuildAndCheckOutput(makeOverallResult) + } + + private fun readModuleDependencies(): Map>? { + val dependenciesTxt = File(testDataDir, "dependencies.txt") + if (!dependenciesTxt.exists()) return null + + val result = HashMap>() + for (line in dependenciesTxt.readLines()) { + val split = line.split("->") + val module = split[0] + val dependencies = if (split.size > 1) split[1] else "" + val dependencyList = dependencies.split(",").filterNot { it.isEmpty() } + result[module] = dependencyList.map(::parseDependency) + } + + return result + } + + protected open fun createBuildLog(incrementalMakeResults: List): String = + buildString { + incrementalMakeResults.forEachIndexed { i, makeResult -> + if (i > 0) append("\n") + append("================ Step #${i + 1} =================\n\n") + append(makeResult.log) + } + } + + protected open fun doTest(testDataPath: String) { + testDataDir = File(testDataPath) + workDir = FileUtilRt.createTempDirectory(TEMP_DIRECTORY_TO_USE, "jps-build", null) + Disposer.register(testRootDisposable, Disposable { FileUtilRt.delete(workDir) }) + + val moduleNames = configureModules() + initialMake() + + val otherMakeResults = performModificationsAndMake(moduleNames) + val buildLogFile = buildLogFinder.findBuildLog(testDataDir) + val logs = createBuildLog(otherMakeResults) + + if (buildLogFile != null && buildLogFile.exists()) { + UsefulTestCase.assertSameLinesWithFile(buildLogFile.absolutePath, logs) + } + else if (!allowNoBuildLogFileInTestData) { + throw IllegalStateException("No build log file in $testDataDir") + } + + val lastMakeResult = otherMakeResults.last() + rebuildAndCheckOutput(lastMakeResult) + clearCachesRebuildAndCheckOutput(lastMakeResult) + } + + private fun createMappingsDump(project: ProjectDescriptor) = + createKotlinIncrementalCacheDump(project) + "\n\n\n" + + createLookupCacheDump(project) + "\n\n\n" + + createCommonMappingsDump(project) + "\n\n\n" + + createJavaMappingsDump(project) + + private fun createKotlinIncrementalCacheDump(project: ProjectDescriptor): String { + return buildString { + for (target in project.allModuleTargets.sortedBy { it.presentableName }) { + append("\n") + append(project.dataManager.getKotlinCache(target).dump()) + append("\n\n\n") + } + } + } + + private fun createLookupCacheDump(project: ProjectDescriptor): String { + val sb = StringBuilder() + val p = Printer(sb) + p.println("Begin of Lookup Maps") + p.println() + + project.dataManager.withLookupStorage { lookupStorage -> + lookupStorage.forceGC() + p.print(lookupStorage.dump(lookupsDuringTest)) + } + + p.println() + p.println("End of Lookup Maps") + return sb.toString() + } + + private fun createCommonMappingsDump(project: ProjectDescriptor): String { + val resultBuf = StringBuilder() + val result = Printer(resultBuf) + + result.println("Begin of SourceToOutputMap") + result.pushIndent() + + for (target in project.allModuleTargets) { + result.println(target) + result.pushIndent() + + val mapping = project.dataManager.getSourceToOutputMap(target) + mapping.sources.sorted().forEach { + val outputs = mapping.getOutputs(it)!!.sorted() + if (outputs.isNotEmpty()) { + result.println("source $it -> $outputs") + } + } + + result.popIndent() + } + + result.popIndent() + result.println("End of SourceToOutputMap") + + return resultBuf.toString() + } + + private fun createJavaMappingsDump(project: ProjectDescriptor): String { + val byteArrayOutputStream = ByteArrayOutputStream() + PrintStream(byteArrayOutputStream).use { + project.dataManager.mappings.toStream(it) + } + return byteArrayOutputStream.toString() + } + + protected data class MakeResult(val log: String, val makeFailed: Boolean, val mappingsDump: String?) + + private fun performModificationsAndMake(moduleNames: Set?): List { + val results = arrayListOf() + val modifications = getModificationsToPerform(testDataDir, moduleNames, allowNoFilesWithSuffixInTestData, TouchPolicy.TIMESTAMP) + + for (step in modifications) { + step.forEach { it.perform(workDir, mapWorkingToOriginalFile) } + performAdditionalModifications(step) + if (moduleNames == null) { + preProcessSources(File(workDir, "src")) + } + else { + moduleNames.forEach { preProcessSources(File(workDir, "$it/src")) } + } + + results.add(make()) + } + return results + } + + protected open fun performAdditionalModifications(modifications: List) { + } + + // null means one module + private fun configureModules(): Set? { + fun prepareModuleSources(moduleName: String?) { + val sourceDirName = moduleName?.let { "$it/src" } ?: "src" + val filePrefix = moduleName?.let { "${it}_" } ?: "" + val sourceDestinationDir = File(workDir, sourceDirName) + val sourcesMapping = copyTestSources(testDataDir, sourceDestinationDir, filePrefix) + mapWorkingToOriginalFile.putAll(sourcesMapping) + preProcessSources(sourceDestinationDir) + } + + JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(myProject).outputUrl = JpsPathUtil.pathToUrl(getAbsolutePath("out")) + + val jdk = addJdk("my jdk") + val moduleDependencies = readModuleDependencies() + mapWorkingToOriginalFile = hashMapOf() + + val moduleNames: Set? + if (moduleDependencies == null) { + addModule("module", arrayOf(getAbsolutePath("src")), null, null, jdk) + prepareModuleSources(moduleName = null) + moduleNames = null + } + else { + val nameToModule = moduleDependencies.keys + .keysToMap { addModule(it, arrayOf(getAbsolutePath("$it/src")), null, null, jdk)!! } + + for ((moduleName, dependencies) in moduleDependencies) { + val module = nameToModule[moduleName]!! + + for (dependency in dependencies) { + JpsModuleRootModificationUtil.addDependency(module, nameToModule[dependency.name], + JpsJavaDependencyScope.COMPILE, dependency.exported) + } + } + + for (module in nameToModule.values) { + prepareModuleSources(module.name) + } + + moduleNames = nameToModule.keys + } + AbstractKotlinJpsBuildTestCase.addKotlinStdlibDependency(myProject) + AbstractKotlinJpsBuildTestCase.addKotlinTestDependency(myProject) + return moduleNames + } + + + protected open fun preProcessSources(srcDir: File) { + } + + override fun doGetProjectDir(): File? = workDir + + private class MyLogger(val rootPath: String) : ProjectBuilderLoggerBase(), BuildLogger { + private val markedDirtyBeforeRound = ArrayList() + private val markedDirtyAfterRound = ArrayList() + + override fun actionsOnCacheVersionChanged(actions: List) { + if (actions.size > 1 && actions.any { it != CacheVersion.Action.DO_NOTHING }) { + logLine("Actions after cache changed: $actions") + } + } + + override fun markedAsDirtyBeforeRound(files: Iterable) { + markedDirtyBeforeRound.addAll(files) + } + + override fun markedAsDirtyAfterRound(files: Iterable) { + markedDirtyAfterRound.addAll(files) + } + + override fun buildStarted(context: CompileContext, chunk: ModuleChunk) { + if (!chunk.isDummy(context) && context.projectDescriptor.project.modules.size > 1) { + logLine("Building ${chunk.modules.sortedBy { it.name }.joinToString { it.name }}") + } + } + + override fun afterBuildStarted(context: CompileContext, chunk: ModuleChunk) { + logDirtyFiles(markedDirtyBeforeRound) + } + + override fun buildFinished(exitCode: ModuleLevelBuilder.ExitCode) { + logDirtyFiles(markedDirtyAfterRound) + logLine("Exit code: $exitCode") + logLine("------------------------------------------") + } + + private fun logDirtyFiles(files: MutableList) { + if (files.isEmpty()) return + + logLine("Marked as dirty by Kotlin:") + files.apply { + map { FileUtil.toSystemIndependentName(it.path) } + .sorted() + .forEach { logLine(it) } + + clear() + } + } + + private val logBuf = StringBuilder() + val log: String + get() = logBuf.toString() + + val compiledFiles = hashSetOf() + + override fun isEnabled(): Boolean = true + + override fun logCompiledFiles(files: MutableCollection?, builderName: String?, description: String?) { + super.logCompiledFiles(files, builderName, description) + + if (builderName == KotlinBuilder.KOTLIN_BUILDER_NAME) { + compiledFiles.addAll(files!!) + } + } + + override fun logLine(message: String?) { + logBuf.append(KotlinTestUtils.replaceHashWithStar(message!!.replace("^$rootPath/".toRegex(), " "))).append('\n') + } + } +} + +private class MockConstantSearch(private val workDir: File) : Callbacks.ConstantAffectionResolver { + override fun request( + ownerClassName: String, + fieldName: String, + accessFlags: Int, + fieldRemoved: Boolean, + accessChanged: Boolean + ): Future { + val affectedFiles = workDir.walk().filter { it.isFile && it.isNameUsage() } + return FixedFuture(Callbacks.ConstantAffection(affectedFiles.toList())) + } + + private fun File.isNameUsage(): Boolean = + name.equals("usage.kt", ignoreCase = true) + || name.equals("usage.java", ignoreCase = true) +} + +internal val ProjectDescriptor.allModuleTargets: Collection + get() = buildTargetIndex.allTargets.filterIsInstance() + +private class DependencyDescriptor(val name: String, val exported: Boolean) + +private fun parseDependency(dependency: String): DependencyDescriptor = + DependencyDescriptor(dependency.removeSuffix(EXPORTED_SUFFIX), dependency.endsWith(EXPORTED_SUFFIX)) + +private val EXPORTED_SUFFIX = "[exported]" diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt index 7a05d9e9c08..fbb7ce3022b 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt @@ -16,11 +16,6 @@ package org.jetbrains.kotlin.jps.build -import org.jetbrains.jps.builders.java.dependencyView.Callbacks -import com.intellij.util.concurrency.FixedFuture -import java.io.File -import java.util.concurrent.Future - class IncrementalConstantSearchTest : AbstractIncrementalJpsTest() { fun testJavaConstantChangedUsedInKotlin() { doTest("jps-plugin/testData/incremental/custom/javaConstantChangedUsedInKotlin/") @@ -45,26 +40,4 @@ class IncrementalConstantSearchTest : AbstractIncrementalJpsTest() { fun testKotlinJvmFieldUnchangedUsedInJava() { doTest("jps-plugin/testData/incremental/custom/kotlinJvmFieldUnchangedUsedInJava/") } - - override val mockConstantSearch: Callbacks.ConstantAffectionResolver? - get() = object : Callbacks.ConstantAffectionResolver { - override fun request( - ownerClassName: String?, - fieldName: String?, - accessFlags: Int, - fieldRemoved: Boolean, - accessChanged: Boolean - ): Future { - // We emulate how constant affection service works in IDEA: - // it is able to find Kotlin usages of Java constant, but can't find Java usages of Kotlin constant - val affectedFiles = - when { - ownerClassName == "JavaClass" && fieldName == "CONST" -> listOf(File(workDir, "src/usage.kt")) - ownerClassName == "test.Klass" && fieldName == "CONST" -> listOf(File(workDir, "src/Usage.java")) - else -> emptyList() - } - - return FixedFuture(Callbacks.ConstantAffection(affectedFiles)) - } - } } \ No newline at end of file diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt.173 b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt.173 new file mode 100644 index 00000000000..7a05d9e9c08 --- /dev/null +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/IncrementalConstantSearchTest.kt.173 @@ -0,0 +1,70 @@ +/* + * 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.jps.build + +import org.jetbrains.jps.builders.java.dependencyView.Callbacks +import com.intellij.util.concurrency.FixedFuture +import java.io.File +import java.util.concurrent.Future + +class IncrementalConstantSearchTest : AbstractIncrementalJpsTest() { + fun testJavaConstantChangedUsedInKotlin() { + doTest("jps-plugin/testData/incremental/custom/javaConstantChangedUsedInKotlin/") + } + + fun testJavaConstantUnchangedUsedInKotlin() { + doTest("jps-plugin/testData/incremental/custom/javaConstantUnchangedUsedInKotlin/") + } + + fun testKotlinConstantChangedUsedInJava() { + doTest("jps-plugin/testData/incremental/custom/kotlinConstantChangedUsedInJava/") + } + + fun testKotlinJvmFieldChangedUsedInJava() { + doTest("jps-plugin/testData/incremental/custom/kotlinJvmFieldChangedUsedInJava/") + } + + fun testKotlinConstantUnchangedUsedInJava() { + doTest("jps-plugin/testData/incremental/custom/kotlinConstantUnchangedUsedInJava/") + } + + fun testKotlinJvmFieldUnchangedUsedInJava() { + doTest("jps-plugin/testData/incremental/custom/kotlinJvmFieldUnchangedUsedInJava/") + } + + override val mockConstantSearch: Callbacks.ConstantAffectionResolver? + get() = object : Callbacks.ConstantAffectionResolver { + override fun request( + ownerClassName: String?, + fieldName: String?, + accessFlags: Int, + fieldRemoved: Boolean, + accessChanged: Boolean + ): Future { + // We emulate how constant affection service works in IDEA: + // it is able to find Kotlin usages of Java constant, but can't find Java usages of Kotlin constant + val affectedFiles = + when { + ownerClassName == "JavaClass" && fieldName == "CONST" -> listOf(File(workDir, "src/usage.kt")) + ownerClassName == "test.Klass" && fieldName == "CONST" -> listOf(File(workDir, "src/Usage.java")) + else -> emptyList() + } + + return FixedFuture(Callbacks.ConstantAffection(affectedFiles)) + } + } +} \ No newline at end of file diff --git a/jps-plugin/src/META-INF/services/org.jetbrains.jps.builders.java.JavaBuilderExtension b/jps-plugin/src/META-INF/services/org.jetbrains.jps.builders.java.JavaBuilderExtension new file mode 100644 index 00000000000..206b2c9a36d --- /dev/null +++ b/jps-plugin/src/META-INF/services/org.jetbrains.jps.builders.java.JavaBuilderExtension @@ -0,0 +1 @@ +org.jetbrains.kotlin.jps.build.KotlinJavaBuilderExtension \ No newline at end of file diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinJavaBuilderExtension.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinJavaBuilderExtension.kt new file mode 100644 index 00000000000..63c9e0ba471 --- /dev/null +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinJavaBuilderExtension.kt @@ -0,0 +1,65 @@ +/* + * 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.jps.build + +import org.jetbrains.jps.api.BasicFuture +import org.jetbrains.jps.builders.java.JavaBuilderExtension +import org.jetbrains.jps.builders.java.dependencyView.Callbacks +import org.jetbrains.jps.incremental.CompileContext +import org.jetbrains.kotlin.incremental.LookupSymbol +import org.jetbrains.kotlin.jps.incremental.withLookupStorage +import java.io.File +import java.util.concurrent.Executors +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit + +class KotlinJavaBuilderExtension : JavaBuilderExtension() { + override fun getConstantSearch(context: CompileContext): Callbacks.ConstantAffectionResolver { + return KotlinLookupConstantSearch(context) + } +} + +private class KotlinLookupConstantSearch(context: CompileContext) : Callbacks.ConstantAffectionResolver { + private val pool = Executors.newSingleThreadExecutor() + private val dataManager = context.projectDescriptor.dataManager + + override fun request( + ownerClassName: String, + fieldName: String, + accessFlags: Int, + fieldRemoved: Boolean, + accessChanged: Boolean + ): Future { + val future = object : BasicFuture() { + @Volatile + private var result: Callbacks.ConstantAffection = Callbacks.ConstantAffection.EMPTY + + fun result(files: Collection) { + result = Callbacks.ConstantAffection(files) + setDone() + } + + override fun get(): Callbacks.ConstantAffection { + super.get() + return result + } + + override fun get(timeout: Long, unit: TimeUnit): Callbacks.ConstantAffection { + super.get(timeout, unit) + return result + } + } + pool.submit { + if (!future.isCancelled) { + dataManager.withLookupStorage { storage -> + val paths = storage.get(LookupSymbol(name = fieldName, scope = ownerClassName)) + future.result(paths.map { File(it) }) + } + } + } + return future + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-script-util/build.gradle.kts b/libraries/tools/kotlin-script-util/build.gradle.kts index 664346a6efe..33a291766ca 100644 --- a/libraries/tools/kotlin-script-util/build.gradle.kts +++ b/libraries/tools/kotlin-script-util/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { testRuntime("org.sonatype.aether:aether-api:1.13.1") testRuntime("org.apache.maven:maven-core:3.0.3") compileOnly(intellijDep()) { includeJars("openapi", "util") } - testCompile(intellijDep()) { includeJars("openapi", "util") } + testCompile(intellijDep()) { includeJars("openapi", "platform-api", "util") } } projectTest { diff --git a/libraries/tools/kotlin-script-util/build.gradle.kts.173 b/libraries/tools/kotlin-script-util/build.gradle.kts.173 new file mode 100644 index 00000000000..664346a6efe --- /dev/null +++ b/libraries/tools/kotlin-script-util/build.gradle.kts.173 @@ -0,0 +1,40 @@ + +description = "Kotlin scripting support utilities" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compile(projectDist(":kotlin-stdlib")) + compile(project(":kotlin-script-runtime")) + compileOnly(project(":compiler:cli")) + compileOnly(project(":compiler:daemon-common")) + compile(projectRuntimeJar(":kotlin-daemon-client")) + compileOnly("com.jcabi:jcabi-aether:0.10.1") + compileOnly("org.sonatype.aether:aether-api:1.13.1") + compileOnly("org.apache.maven:maven-core:3.0.3") + testCompileOnly(project(":compiler:cli")) + testCompile(project(":kotlin-test:kotlin-test-junit")) + testRuntime(project(":kotlin-reflect")) + testCompile(commonDep("junit:junit")) + testRuntime(projectRuntimeJar(":kotlin-compiler")) + testRuntime("com.jcabi:jcabi-aether:0.10.1") + testRuntime("org.sonatype.aether:aether-api:1.13.1") + testRuntime("org.apache.maven:maven-core:3.0.3") + compileOnly(intellijDep()) { includeJars("openapi", "util") } + testCompile(intellijDep()) { includeJars("openapi", "util") } +} + +projectTest { + workingDir = rootDir +} + +runtimeJar() +sourcesJar() +javadocJar() + +publish() + +ideaPlugin() diff --git a/plugins/android-extensions/android-extensions-idea/build.gradle.kts b/plugins/android-extensions/android-extensions-idea/build.gradle.kts index 340db2032d0..6f41bc0d7f6 100644 --- a/plugins/android-extensions/android-extensions-idea/build.gradle.kts +++ b/plugins/android-extensions/android-extensions-idea/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { testRuntime(intellijPluginDep("java-decompiler")) testRuntime(intellijPluginDep("maven")) testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) } sourceSets { diff --git a/plugins/android-extensions/android-extensions-idea/build.gradle.kts.173 b/plugins/android-extensions/android-extensions-idea/build.gradle.kts.173 new file mode 100644 index 00000000000..340db2032d0 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/build.gradle.kts.173 @@ -0,0 +1,74 @@ + +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")) +} + +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-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt index 548206fdc8e..d099fa2d4f8 100644 --- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.android.model.impl import com.android.builder.model.SourceProvider import com.android.tools.idea.gradle.project.GradleProjectInfo import com.android.tools.idea.gradle.project.model.AndroidModuleModel +import com.android.tools.idea.res.AppResourceRepository import com.intellij.openapi.module.Module import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.android.facet.AndroidFacet @@ -35,7 +36,7 @@ class AndroidModuleInfoProviderImpl(override val module: Module) : AndroidModule } override fun getApplicationResourceDirectories(createIfNecessary: Boolean): Collection { - return androidFacet?.getAppResources(createIfNecessary)?.resourceDirs ?: emptyList() + return AppResourceRepository.getOrCreateInstance(module)?.resourceDirs ?: emptyList() } override fun getAllSourceProviders(): List { diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt.173 b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt.173 new file mode 100644 index 00000000000..548206fdc8e --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/model/impl/AndroidModuleInfoProviderImpl.kt.173 @@ -0,0 +1,77 @@ +/* + * 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.android.model.impl + +import com.android.builder.model.SourceProvider +import com.android.tools.idea.gradle.project.GradleProjectInfo +import com.android.tools.idea.gradle.project.model.AndroidModuleModel +import com.intellij.openapi.module.Module +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.android.model.AndroidModuleInfoProvider +import java.io.File + +class AndroidModuleInfoProviderImpl(override val module: Module) : AndroidModuleInfoProvider { + private val androidFacet: AndroidFacet? + get() = AndroidFacet.getInstance(module) + + private val androidModuleModel: AndroidModuleModel? + get() = AndroidModuleModel.get(module) + + override fun isAndroidModule() = androidFacet != null + override fun isGradleModule() = GradleProjectInfo.getInstance(module.project).isBuildWithGradle + + override fun getAllResourceDirectories(): List { + return androidFacet?.allResourceDirectories ?: emptyList() + } + + override fun getApplicationPackage() = androidFacet?.manifest?.`package`?.toString() + + override fun getMainSourceProvider(): AndroidModuleInfoProvider.SourceProviderMirror? { + return androidFacet?.mainSourceProvider?.let(::SourceProviderMirrorImpl) + } + + override fun getApplicationResourceDirectories(createIfNecessary: Boolean): Collection { + return androidFacet?.getAppResources(createIfNecessary)?.resourceDirs ?: emptyList() + } + + override fun getAllSourceProviders(): List { + val androidModuleModel = this.androidModuleModel ?: return emptyList() + return androidModuleModel.allSourceProviders.map(::SourceProviderMirrorImpl) + } + + override fun getActiveSourceProviders(): List { + val androidModuleModel = this.androidModuleModel ?: return emptyList() + return androidModuleModel.activeSourceProviders.map(::SourceProviderMirrorImpl) + } + + override fun getFlavorSourceProviders(): List { + val androidModuleModel = this.androidModuleModel ?: return emptyList() + + val getFlavorSourceProvidersMethod = try { + AndroidFacet::class.java.getMethod("getFlavorSourceProviders") + } catch (e: NoSuchMethodException) { + null + } + + return if (getFlavorSourceProvidersMethod != null) { + @Suppress("UNCHECKED_CAST") + val sourceProviders = getFlavorSourceProvidersMethod.invoke(androidFacet) as? List + sourceProviders?.map(::SourceProviderMirrorImpl) ?: emptyList() + } else { + androidModuleModel.flavorSourceProviders.map(::SourceProviderMirrorImpl) + } + } + + private class SourceProviderMirrorImpl(val sourceProvider: SourceProvider) : + AndroidModuleInfoProvider.SourceProviderMirror { + override val name: String + get() = sourceProvider.name + + override val resDirectories: Collection + get() = sourceProvider.resDirectories + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-jps/build.gradle.kts b/plugins/android-extensions/android-extensions-jps/build.gradle.kts index 7fb8d78f14e..4a63b603e9b 100644 --- a/plugins/android-extensions/android-extensions-jps/build.gradle.kts +++ b/plugins/android-extensions/android-extensions-jps/build.gradle.kts @@ -10,7 +10,7 @@ dependencies { compile(project(":compiler:util")) compile(project(":jps-plugin")) compile(project(":plugins:android-extensions-compiler")) - compileOnly(intellijDep()) { includeJars("openapi", "jps-builders", "jps-model", "jdom") } + compileOnly(intellijDep()) { includeJars("openapi", "platform-api", "jps-builders", "jps-model", "jdom") } compileOnly(intellijPluginDep("android")) { includeJars("jps/android-jps-plugin") } compile(intellijPluginDep("android")) { includeJars("jps/android-jps-plugin") } @@ -24,6 +24,7 @@ dependencies { testCompileOnly(intellijDep()) { includeJars("jps-model") } testRuntime(intellijPluginDep("android")) + testRuntime(intellijPluginDep("smali")) testRuntime(intellijDep("jps-build-test")) testRuntime(intellijDep("jps-standalone")) } diff --git a/plugins/android-extensions/android-extensions-jps/build.gradle.kts.173 b/plugins/android-extensions/android-extensions-jps/build.gradle.kts.173 new file mode 100644 index 00000000000..7fb8d78f14e --- /dev/null +++ b/plugins/android-extensions/android-extensions-jps/build.gradle.kts.173 @@ -0,0 +1,41 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testRuntime(intellijDep()) + + compile(project(":compiler:util")) + compile(project(":jps-plugin")) + compile(project(":plugins:android-extensions-compiler")) + compileOnly(intellijDep()) { includeJars("openapi", "jps-builders", "jps-model", "jdom") } + compileOnly(intellijPluginDep("android")) { includeJars("jps/android-jps-plugin") } + compile(intellijPluginDep("android")) { includeJars("jps/android-jps-plugin") } + + testCompile(projectTests(":jps-plugin")) + testCompile(project(":compiler:tests-common")) + testCompile(commonDep("junit:junit")) + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(projectTests(":kotlin-build-common")) + testCompileOnly(intellijDep()) { includeJars("openapi", "jps-builders") } + testCompileOnly(intellijDep("jps-build-test")) { includeJars("jps-build-test") } + testCompileOnly(intellijDep()) { includeJars("jps-model") } + + testRuntime(intellijPluginDep("android")) + testRuntime(intellijDep("jps-build-test")) + testRuntime(intellijDep("jps-standalone")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +projectTest { + workingDir = rootDir + useAndroidSdk() +} + +testsJar {} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-compiler/build.gradle.kts b/plugins/kapt3/kapt3-compiler/build.gradle.kts index aa4891a4b9e..0b14357d8ed 100644 --- a/plugins/kapt3/kapt3-compiler/build.gradle.kts +++ b/plugins/kapt3/kapt3-compiler/build.gradle.kts @@ -9,7 +9,7 @@ plugins { dependencies { testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") } testRuntime(intellijDep()) - testCompileOnly(intellijDep()) { includeJars("idea", "idea_rt", "openapi") } + testCompileOnly(intellijDep()) { includeJars("idea", "idea_rt", "openapi", "platform-api", "platform-impl") } compile(project(":compiler:util")) compile(project(":compiler:cli")) diff --git a/plugins/kapt3/kapt3-compiler/build.gradle.kts.173 b/plugins/kapt3/kapt3-compiler/build.gradle.kts.173 new file mode 100644 index 00000000000..aa4891a4b9e --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/build.gradle.kts.173 @@ -0,0 +1,53 @@ + +description = "Annotation Processor for Kotlin" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") } + testRuntime(intellijDep()) + testCompileOnly(intellijDep()) { includeJars("idea", "idea_rt", "openapi") } + + compile(project(":compiler:util")) + compile(project(":compiler:cli")) + compile(project(":compiler:backend")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":compiler:plugin-api")) + compileOnly(project(":kotlin-annotation-processing-runtime")) + compileOnly(intellijCoreDep()) { includeJars("intellij-core") } + compileOnly(intellijDep()) { includeJars("asm-all") } + + testCompile(project(":compiler:tests-common")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(commonDep("junit:junit")) + testCompile(project(":kotlin-annotation-processing-runtime")) + + embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false } +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +testsJar {} + +projectTest { + workingDir = rootDir + dependsOn(":dist") +} + +runtimeJar { + fromEmbeddedComponents() +} + +sourcesJar() +javadocJar() + +dist() + +publish() diff --git a/plugins/lint/android-annotations/src/com/android/annotations/NonNull.java b/plugins/lint/android-annotations/src/com/android/annotations/NonNull.java.173 similarity index 100% rename from plugins/lint/android-annotations/src/com/android/annotations/NonNull.java rename to plugins/lint/android-annotations/src/com/android/annotations/NonNull.java.173 diff --git a/plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java b/plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java.173 similarity index 100% rename from plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java rename to plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java.173 diff --git a/plugins/lint/android-annotations/src/com/android/annotations/Nullable.java b/plugins/lint/android-annotations/src/com/android/annotations/Nullable.java.173 old mode 100755 new mode 100644 similarity index 100% rename from plugins/lint/android-annotations/src/com/android/annotations/Nullable.java rename to plugins/lint/android-annotations/src/com/android/annotations/Nullable.java.173 diff --git a/plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java b/plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java.173 old mode 100755 new mode 100644 similarity index 100% rename from plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java rename to plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java.173 diff --git a/plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java b/plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java.173 similarity index 100% rename from plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java rename to plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java.173 diff --git a/plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java b/plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java.173 similarity index 100% rename from plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java rename to plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AndroidReference.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AndroidReference.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/AndroidReference.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/AndroidReference.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ExternalReferenceExpression.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ExternalReferenceExpression.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/ExternalReferenceExpression.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/ExternalReferenceExpression.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaEvaluator.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaEvaluator.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaEvaluator.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaEvaluator.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaPsiVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaPsiVisitor.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaPsiVisitor.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaPsiVisitor.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaVisitor.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaVisitor.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaVisitor.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/UElementVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/UElementVisitor.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/UElementVisitor.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/UElementVisitor.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/UastLintUtils.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/UastLintUtils.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/UastLintUtils.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/UastLintUtils.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java rename to plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ConstantEvaluator.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ConstantEvaluator.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ConstantEvaluator.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ConstantEvaluator.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceEvaluator.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceEvaluator.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceEvaluator.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceEvaluator.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TypeEvaluator.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TypeEvaluator.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TypeEvaluator.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TypeEvaluator.java.173 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java.173 similarity index 100% rename from plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java rename to plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AllowAllHostnameVerifierDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AllowAllHostnameVerifierDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AllowAllHostnameVerifierDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AllowAllHostnameVerifierDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AndroidAutoDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AndroidAutoDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AndroidAutoDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AndroidAutoDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiPackage.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiPackage.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiPackage.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiPackage.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BadHostnameVerifierDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BadHostnameVerifierDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/BadHostnameVerifierDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/BadHostnameVerifierDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BatteryDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BatteryDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/BatteryDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/BatteryDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LeakDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LeakDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/LeakDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/LeakDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionFinder.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionFinder.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionFinder.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionFinder.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ReadParcelableDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ReadParcelableDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ReadParcelableDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ReadParcelableDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RecyclerViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RecyclerViewDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/RecyclerViewDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/RecyclerViewDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetTextDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetTextDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetTextDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetTextDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SslCertificateSocketFactoryDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SslCertificateSocketFactoryDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SslCertificateSocketFactoryDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SslCertificateSocketFactoryDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringAuthLeakDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringAuthLeakDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringAuthLeakDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringAuthLeakDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TrustAllX509TrustManagerDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TrustAllX509TrustManagerDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/TrustAllX509TrustManagerDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/TrustAllX509TrustManagerDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeBroadcastReceiverDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeBroadcastReceiverDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeBroadcastReceiverDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeBroadcastReceiverDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeNativeCodeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeNativeCodeDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeNativeCodeDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnsafeNativeCodeDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java.173 diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/api-versions-support-library.xml b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/api-versions-support-library.xml.173 similarity index 100% rename from plugins/lint/lint-checks/src/com/android/tools/klint/checks/api-versions-support-library.xml rename to plugins/lint/lint-checks/src/com/android/tools/klint/checks/api-versions-support-library.xml.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetVersionCheckQuickFix.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetVersionCheckQuickFix.kt.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetVersionCheckQuickFix.kt rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetVersionCheckQuickFix.kt.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ApiUtils.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ApiUtils.kt.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ApiUtils.kt rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ApiUtils.kt.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IdeaJavaParser.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IdeaJavaParser.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IdeaJavaParser.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IdeaJavaParser.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java.173 diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt.173 similarity index 100% rename from plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt rename to plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt.173 diff --git a/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts b/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts index 0603f259ac8..27b6f93e9e2 100644 --- a/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts +++ b/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { compile(project(":idea:idea-android")) compile(project(":idea")) compile(project(":idea:idea-jvm")) - compile(intellijDep()) { includeJars("openapi", "extensions", "util") } + compile(intellijDep()) { includeJars("openapi", "platform-api", "extensions", "util") } } sourceSets { diff --git a/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts.173 b/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts.173 new file mode 100644 index 00000000000..0603f259ac8 --- /dev/null +++ b/plugins/sam-with-receiver/sam-with-receiver-ide/build.gradle.kts.173 @@ -0,0 +1,32 @@ + +description = "Kotlin SamWithReceiver IDEA Plugin" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +jvmTarget = "1.6" + +dependencies { + compile(project(":kotlin-sam-with-receiver-compiler-plugin")) + compile(project(":plugins:annotation-based-compiler-plugins-ide-support")) + compile(project(":compiler:util")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":idea:idea-core")) + compile(project(":idea:idea-android")) + compile(project(":idea")) + compile(project(":idea:idea-jvm")) + compile(intellijDep()) { includeJars("openapi", "extensions", "util") } +} + +sourceSets { + "main" { projectDefault() } + "test" {} +} + +runtimeJar() + +ideaPlugin() + diff --git a/plugins/uast-kotlin/build.gradle.kts b/plugins/uast-kotlin/build.gradle.kts index 854fb20e8b4..3c93d9440e0 100644 --- a/plugins/uast-kotlin/build.gradle.kts +++ b/plugins/uast-kotlin/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { compile(project(":compiler:frontend.java")) compile(project(":compiler:light-classes")) compile(project(":idea:idea-core")) - compileOnly(intellijDep()) { includeJars("openapi", "idea", "util", "extensions", "asm-all") } + compileOnly(intellijDep()) { includeJars("openapi", "idea", "java-api", "java-impl", "platform-api", "platform-impl", "util", "extensions", "asm-all") } testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) testCompile(projectTests(":compiler:tests-common")) @@ -20,7 +20,7 @@ dependencies { testCompile(project(":compiler:util")) testCompile(project(":compiler:cli")) testCompile(projectTests(":idea:idea-test-framework")) - testCompileOnly(intellijDep()) { includeJars("idea_rt") } + testCompileOnly(intellijDep()) { includeJars("java-api", "java-impl", "idea_rt") } testRuntime(projectDist(":kotlin-reflect")) testRuntime(project(":idea:idea-android")) diff --git a/plugins/uast-kotlin/build.gradle.kts.173 b/plugins/uast-kotlin/build.gradle.kts.173 new file mode 100644 index 00000000000..854fb20e8b4 --- /dev/null +++ b/plugins/uast-kotlin/build.gradle.kts.173 @@ -0,0 +1,50 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compile(projectDist(":kotlin-stdlib")) + compile(project(":core:util.runtime")) + compile(project(":compiler:backend")) + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":compiler:light-classes")) + compile(project(":idea:idea-core")) + compileOnly(intellijDep()) { includeJars("openapi", "idea", "util", "extensions", "asm-all") } + + testCompile(projectDist(":kotlin-test:kotlin-test-jvm")) + testCompile(projectTests(":compiler:tests-common")) + testCompile(commonDep("junit:junit")) + testCompile(project(":compiler:util")) + testCompile(project(":compiler:cli")) + testCompile(projectTests(":idea:idea-test-framework")) + testCompileOnly(intellijDep()) { includeJars("idea_rt") } + + testRuntime(projectDist(":kotlin-reflect")) + testRuntime(project(":idea:idea-android")) + testRuntime(project(":idea:idea-gradle")) + testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false } + testRuntime(project(":sam-with-receiver-ide-plugin")) + testRuntime(project(":allopen-ide-plugin")) + testRuntime(project(":noarg-ide-plugin")) + testRuntime(project(":plugins:android-extensions-ide")) + testRuntime(project(":plugins:kapt3-idea")) + testRuntime(intellijDep()) + testRuntime(intellijPluginDep("junit")) + testRuntime(intellijPluginDep("gradle")) + testRuntime(intellijPluginDep("Groovy")) + testRuntime(intellijPluginDep("properties")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +testsJar {} + +projectTest { + workingDir = rootDir +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt index 6d49284e33f..5c13c82824f 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.declarations.KotlinUMethod import org.jetbrains.uast.kotlin.expressions.* import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter @@ -292,9 +293,13 @@ internal object KotlinConverter { is KtContainerNode -> unwrapElements(element.parent) is KtSimpleNameStringTemplateEntry -> unwrapElements(element.parent) is KtLightParameterList -> unwrapElements(element.parent) + is KtTypeElement -> unwrapElements(element.parent) else -> element } + private val identifiersTokens = + setOf(KtTokens.IDENTIFIER, KtTokens.CONSTRUCTOR_KEYWORD, KtTokens.THIS_KEYWORD, KtTokens.SUPER_KEYWORD, KtTokens.OBJECT_KEYWORD) + internal fun convertPsiElement(element: PsiElement?, givenParent: UElement?, requiredType: Class?): UElement? { @@ -352,8 +357,10 @@ internal object KotlinConverter { is KtImportDirective -> el(build(::KotlinUImportStatement)) else -> { if (element is LeafPsiElement) { - if (element.elementType == KtTokens.IDENTIFIER) - el(build(::UIdentifier)) + if (element.elementType in identifiersTokens) + if (element.elementType != KtTokens.OBJECT_KEYWORD || element.getParentOfType(false)?.nameIdentifier == null) + el(build(::KotlinUIdentifier)) + else null else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression) el { UIdentifier( @@ -365,9 +372,7 @@ internal object KotlinConverter { ) } else null - } else { - null - } + } else null } }} } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.173 new file mode 100644 index 00000000000..6d49284e33f --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.173 @@ -0,0 +1,555 @@ +/* + * 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.uast.kotlin + +import com.intellij.lang.Language +import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.asJava.LightClassUtil +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.elements.* +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.project.TargetPlatformDetector +import org.jetbrains.kotlin.idea.util.module +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUMethod +import org.jetbrains.uast.kotlin.expressions.* +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable + +interface KotlinUastBindingContextProviderService { + fun getBindingContext(element: KtElement): BindingContext + fun getTypeMapper(element: KtElement): KotlinTypeMapper? +} + +var PsiElement.destructuringDeclarationInitializer: Boolean? by UserDataProperty(Key.create("kotlin.uast.destructuringDeclarationInitializer")) + +class KotlinUastLanguagePlugin : UastLanguagePlugin { + override val priority = 10 + + override val language: Language + get() = KotlinLanguage.INSTANCE + + override fun isFileSupported(fileName: String): Boolean { + return fileName.endsWith(".kt", false) || fileName.endsWith(".kts", false) + } + + private val PsiElement.isJvmElement + get() = try { + // Workaround for UAST used without full-fledged IDEA when ProjectFileIndex is not available + // If we can't get the module (or don't have one), act as if the current platform is JVM + val module = module + module == null || TargetPlatformDetector.getPlatform(module) is JvmPlatform + } catch (e: Exception) { + true + } + + override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class?): UElement? { + if (!element.isJvmElement) return null + return convertDeclarationOrElement(element, parent, requiredType) + } + + override fun convertElementWithParent(element: PsiElement, requiredType: Class?): UElement? { + if (!element.isJvmElement) return null + if (element is PsiFile) return convertDeclaration(element, null, requiredType) + if (element is KtLightClassForFacade) return convertDeclaration(element, null, requiredType) + + return convertDeclarationOrElement(element, null, requiredType) + } + + private fun convertDeclarationOrElement(element: PsiElement, givenParent: UElement?, requiredType: Class?): UElement? { + if (element is UElement) return element + + if (element.isValid) { + element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement -> + return if (requiredType == null || requiredType.isInstance(cachedUElement)) cachedUElement else null + } + } + + val uElement = convertDeclaration(element, givenParent, requiredType) + ?: KotlinConverter.convertPsiElement(element, givenParent, requiredType) + /* + if (uElement != null) { + element.putUserData(KOTLIN_CACHED_UELEMENT_KEY, WeakReference(uElement)) + } + */ + return uElement + } + + override fun getMethodCallExpression( + element: PsiElement, + containingClassFqName: String?, + methodName: String + ): UastLanguagePlugin.ResolvedMethod? { + if (element !is KtCallExpression) return null + val resolvedCall = element.getResolvedCall(element.analyze()) ?: return null + val resultingDescriptor = resolvedCall.resultingDescriptor + if (resultingDescriptor !is FunctionDescriptor || resultingDescriptor.name.asString() != methodName) return null + + val parent = element.parent + val parentUElement = convertElementWithParent(parent, null) ?: return null + + val uExpression = KotlinUFunctionCallExpression(element, parentUElement, resolvedCall) + val method = uExpression.resolve() ?: return null + if (method.name != methodName) return null + return UastLanguagePlugin.ResolvedMethod(uExpression, method) + } + + override fun getConstructorCallExpression( + element: PsiElement, + fqName: String + ): UastLanguagePlugin.ResolvedConstructor? { + if (element !is KtCallExpression) return null + val resolvedCall = element.getResolvedCall(element.analyze()) ?: return null + val resultingDescriptor = resolvedCall.resultingDescriptor + if (resultingDescriptor !is ConstructorDescriptor + || resultingDescriptor.returnType.constructor.declarationDescriptor?.name?.asString() != fqName) { + return null + } + + val parent = KotlinConverter.unwrapElements(element.parent) ?: return null + val parentUElement = convertElementWithParent(parent, null) ?: return null + + val uExpression = KotlinUFunctionCallExpression(element, parentUElement, resolvedCall) + val method = uExpression.resolve() ?: return null + val containingClass = method.containingClass ?: return null + return UastLanguagePlugin.ResolvedConstructor(uExpression, method, containingClass) + } + + internal fun convertDeclaration(element: PsiElement, + givenParent: UElement?, + requiredType: Class?): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) } + + fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = + { ctor(element as P, ktElement, givenParent) } + + fun

buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? = + { ctor(element as P, ktElement, givenParent) } + + val original = element.originalElement + return with(requiredType) { + when (original) { + is KtLightMethod -> el(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 + is KtLightClass -> when (original.kotlinOrigin) { + is KtEnumEntry -> el { + convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent) + } + else -> el { KotlinUClass.create(original, givenParent) } + } + is KtLightFieldImpl.KtLightEnumConstant -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant)) + is KtLightField -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) + is KtLightParameter -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) + is UastKotlinPsiParameter -> el(buildKt(original.ktParameter, ::KotlinUParameter)) + is UastKotlinPsiVariable -> el(buildKt(original.ktElement, ::KotlinUVariable)) + + is KtEnumEntry -> el { + convertEnumEntry(original, givenParent) + } + is KtClassOrObject -> el { + original.toLightClass()?.let { lightClass -> + KotlinUClass.create(lightClass, givenParent) + } + } + is KtFunction -> + if (original.isLocal) { + el { + if (original.name.isNullOrEmpty() || original.parent is KtLambdaExpression) { + createLocalFunctionLambdaExpression(original, givenParent) + } + else { + val uDeclarationsExpression = createLocalFunctionDeclaration(original, givenParent) + val localFunctionVar = uDeclarationsExpression.declarations.single() as KotlinLocalFunctionUVariable + localFunctionVar.uastInitializer + } + } + } + else { + el { + val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null + convertDeclaration(lightMethod, givenParent, requiredType) + } + } + + is KtPropertyAccessor -> el { + val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null + convertDeclaration(lightMethod, givenParent, requiredType) + } + + is KtProperty -> + if (original.isLocal) { + KotlinConverter.convertPsiElement(element, givenParent, requiredType) + } + else { + convertNonLocalProperty(original, givenParent, requiredType) + } + + is KtParameter -> el { + val ownerFunction = original.ownerFunction as? KtFunction ?: return null + val lightMethod = LightClassUtil.getLightClassMethod(ownerFunction) ?: return null + val lightParameter = lightMethod.parameterList.parameters.find { it.name == original.name } ?: return null + KotlinUParameter(lightParameter, original, givenParent) + } + + is KtFile -> el { KotlinUFile(original, this@KotlinUastLanguagePlugin) } + is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) } + is KtAnnotationEntry -> el(build(::KotlinUAnnotation)) + is KtCallExpression -> + if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) { + el { + val classDescriptor = + (original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass + if (classDescriptor?.kind == ClassKind.ANNOTATION_CLASS) + KotlinUNestedAnnotation(original, givenParent, classDescriptor) + else + null + } + } else null + is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType) + else -> null + } + } + } + + private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? { + return LightClassUtil.getLightClassBackingField(original)?.let { psiField -> + if (psiField is KtLightFieldImpl.KtLightEnumConstant) { + KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent) + } + else { + null + } + } + } + + override fun isExpressionValueUsed(element: UExpression): Boolean { + return when (element) { + is KotlinUSimpleReferenceExpression.KotlinAccessorCallExpression -> element.setterValue != null + is KotlinAbstractUExpression -> { + val ktElement = element.psi as? KtElement ?: return false + ktElement.analyze()[BindingContext.USED_AS_EXPRESSION, ktElement] ?: false + } + else -> false + } + } +} + +internal inline fun Class?.el(f: () -> UElement?): UElement? { + return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +} + +internal inline fun Class?.expr(f: () -> UExpression?): UExpression? { + return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +} + +private fun convertNonLocalProperty(property: KtProperty, + givenParent: UElement?, + requiredType: Class?): UElement? { + val methods = LightClassUtil.getLightClassPropertyMethods(property) + return methods.backingField?.let { backingField -> + with(requiredType) { + el { KotlinUField(backingField, (backingField as? KtLightElement<*,*>)?.kotlinOrigin, givenParent) } + } + } ?: methods.getter?.let { getter -> + KotlinUastLanguagePlugin().convertDeclaration(getter, givenParent, requiredType) + } +} + +internal object KotlinConverter { + internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) { + is KtValueArgumentList -> unwrapElements(element.parent) + is KtValueArgument -> unwrapElements(element.parent) + is KtDeclarationModifierList -> unwrapElements(element.parent) + is KtContainerNode -> unwrapElements(element.parent) + is KtSimpleNameStringTemplateEntry -> unwrapElements(element.parent) + is KtLightParameterList -> unwrapElements(element.parent) + else -> element + } + + internal fun convertPsiElement(element: PsiElement?, + givenParent: UElement?, + requiredType: Class?): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { + return { ctor(element as P, givenParent) } + } + + return with (requiredType) { when (element) { + is KtParameterList -> el { + val declarationsExpression = KotlinUDeclarationsExpression(givenParent) + declarationsExpression.apply { + declarations = element.parameters.mapIndexed { i, p -> + KotlinUParameter(UastKotlinPsiParameter.create(p, element, declarationsExpression, i), p, this) + } + } + } + is KtClassBody -> el(build(KotlinUExpressionList.Companion::createClassBody)) + is KtCatchClause -> el(build(::KotlinUCatchClause)) + is KtVariableDeclaration -> + if (element is KtProperty && !element.isLocal) { + el { + LightClassUtil.getLightClassBackingField(element)?.let { + KotlinUField(it, element, givenParent) + } + } + } + else { + el { + convertVariablesDeclaration(element, givenParent).declarations.singleOrNull() + } + } + + is KtExpression -> KotlinConverter.convertExpression(element, givenParent, requiredType) + is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) } + is KtLightAnnotationForSourceEntry.LightExpressionValue<*> -> { + val expression = element.originalExpression + when (expression) { + is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, requiredType) + else -> el { UastEmptyExpression } + } + } + is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el(build(::KotlinStringULiteralExpression)) + is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, requiredType) } ?: expr { UastEmptyExpression } + is KtWhenEntry -> el(build(::KotlinUSwitchEntry)) + is KtWhenCondition -> convertWhenCondition(element, givenParent, requiredType) + is KtTypeReference -> el { LazyKotlinUTypeReferenceExpression(element, givenParent) } + is KtConstructorDelegationCall -> + el { KotlinUFunctionCallExpression(element, givenParent) } + is KtSuperTypeCallEntry -> + el { + (element.getParentOfType(true)?.parent as? KtObjectLiteralExpression) + ?.toUElementOfType() + ?: KotlinUFunctionCallExpression(element, givenParent) + } + is KtImportDirective -> el(build(::KotlinUImportStatement)) + else -> { + if (element is LeafPsiElement) { + if (element.elementType == KtTokens.IDENTIFIER) + el(build(::UIdentifier)) + else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression) + el { + UIdentifier( + element, + KotlinUCollectionLiteralExpression( + element.parent as KtCollectionLiteralExpression, + null + ) + ) + } + else null + } else { + null + } + } + }} + } + + + internal fun convertEntry(entry: KtStringTemplateEntry, + givenParent: UElement?, + requiredType: Class? = null): UExpression? { + return with(requiredType) { + if (entry is KtStringTemplateEntryWithExpression) { + expr { + KotlinConverter.convertOrEmpty(entry.expression, givenParent) + } + } + else { + expr { + if (entry is KtEscapeStringTemplateEntry) + KotlinStringULiteralExpression(entry, givenParent, entry.unescapedValue) + else + KotlinStringULiteralExpression(entry, givenParent) + } + } + } + } + + internal fun convertExpression(expression: KtExpression, + givenParent: UElement?, + requiredType: Class? = null): UExpression? { + fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { + return { ctor(expression as P, givenParent) } + } + + return with (requiredType) { when (expression) { + is KtVariableDeclaration -> expr(build(::convertVariablesDeclaration)) + + is KtStringTemplateExpression -> { + when { + expression.entries.isEmpty() -> { + expr { KotlinStringULiteralExpression(expression, givenParent, "") } + } + expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType) + else -> { + expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } + } + } + } + is KtDestructuringDeclaration -> expr { + val declarationsExpression = KotlinUDestructuringDeclarationExpression(givenParent, expression) + declarationsExpression.apply { + val tempAssignment = KotlinULocalVariable(UastKotlinPsiVariable.create(expression, declarationsExpression), expression, declarationsExpression) + val destructuringAssignments = expression.entries.mapIndexed { i, entry -> + val psiFactory = KtPsiFactory(expression.project) + val initializer = psiFactory.createAnalyzableExpression("${tempAssignment.name}.component${i + 1}()", + expression.containingFile) + initializer.destructuringDeclarationInitializer = true + KotlinULocalVariable(UastKotlinPsiVariable.create(entry, tempAssignment.psi, declarationsExpression, initializer), entry, declarationsExpression) + } + declarations = listOf(tempAssignment) + destructuringAssignments + } + } + is KtLabeledExpression -> expr(build(::KotlinULabeledExpression)) + is KtClassLiteralExpression -> expr(build(::KotlinUClassLiteralExpression)) + is KtObjectLiteralExpression -> expr(build(::KotlinUObjectLiteralExpression)) + is KtDotQualifiedExpression -> expr(build(::KotlinUQualifiedReferenceExpression)) + is KtSafeQualifiedExpression -> expr(build(::KotlinUSafeQualifiedExpression)) + is KtSimpleNameExpression -> expr(build(::KotlinUSimpleReferenceExpression)) + is KtCallExpression -> expr(build(::KotlinUFunctionCallExpression)) + is KtCollectionLiteralExpression -> expr(build(::KotlinUCollectionLiteralExpression)) + is KtBinaryExpression -> { + if (expression.operationToken == KtTokens.ELVIS) { + expr(build(::createElvisExpression)) + } + else expr(build(::KotlinUBinaryExpression)) + } + is KtParenthesizedExpression -> expr(build(::KotlinUParenthesizedExpression)) + is KtPrefixExpression -> expr(build(::KotlinUPrefixExpression)) + is KtPostfixExpression -> expr(build(::KotlinUPostfixExpression)) + is KtThisExpression -> expr(build(::KotlinUThisExpression)) + is KtSuperExpression -> expr(build(::KotlinUSuperExpression)) + is KtCallableReferenceExpression -> expr(build(::KotlinUCallableReferenceExpression)) + is KtIsExpression -> expr(build(::KotlinUTypeCheckExpression)) + is KtIfExpression -> expr(build(::KotlinUIfExpression)) + is KtWhileExpression -> expr(build(::KotlinUWhileExpression)) + is KtDoWhileExpression -> expr(build(::KotlinUDoWhileExpression)) + is KtForExpression -> expr(build(::KotlinUForEachExpression)) + is KtWhenExpression -> expr(build(::KotlinUSwitchExpression)) + is KtBreakExpression -> expr(build(::KotlinUBreakExpression)) + is KtContinueExpression -> expr(build(::KotlinUContinueExpression)) + is KtReturnExpression -> expr(build(::KotlinUReturnExpression)) + is KtThrowExpression -> expr(build(::KotlinUThrowExpression)) + is KtBlockExpression -> expr(build(::KotlinUBlockExpression)) + is KtConstantExpression -> expr(build(::KotlinULiteralExpression)) + is KtTryExpression -> expr(build(::KotlinUTryExpression)) + is KtArrayAccessExpression -> expr(build(::KotlinUArrayAccessExpression)) + is KtLambdaExpression -> expr(build(::KotlinULambdaExpression)) + is KtBinaryExpressionWithTypeRHS -> expr(build(::KotlinUBinaryExpressionWithType)) + is KtClassOrObject -> expr { + expression.toLightClass()?.let { lightClass -> + KotlinUDeclarationsExpression(givenParent).apply { + declarations = listOf(KotlinUClass.create(lightClass, this)) + } + } ?: UastEmptyExpression + } + is KtFunction -> if (expression.name.isNullOrEmpty()) { + expr(build(::createLocalFunctionLambdaExpression)) + } + else { + expr(build(::createLocalFunctionDeclaration)) + } + + else -> expr(build(::UnknownKotlinExpression)) + }} + } + + internal fun convertWhenCondition(condition: KtWhenCondition, + givenParent: UElement?, + requiredType: Class? = null): UExpression? { + return with(requiredType) { + when (condition) { + is KtWhenConditionInRange -> expr { + KotlinCustomUBinaryExpression(condition, givenParent).apply { + leftOperand = KotlinStringUSimpleReferenceExpression("it", this) + operator = when { + condition.isNegated -> KotlinBinaryOperators.NOT_IN + else -> KotlinBinaryOperators.IN + } + rightOperand = KotlinConverter.convertOrEmpty(condition.rangeExpression, this) + } + } + is KtWhenConditionIsPattern -> expr { + KotlinCustomUBinaryExpressionWithType(condition, givenParent).apply { + operand = KotlinStringUSimpleReferenceExpression("it", this) + operationKind = when { + condition.isNegated -> KotlinBinaryExpressionWithTypeKinds.NEGATED_INSTANCE_CHECK + else -> UastBinaryExpressionWithTypeKind.INSTANCE_CHECK + } + val typeRef = condition.typeReference + typeReference = typeRef?.let { + LazyKotlinUTypeReferenceExpression(it, this) { typeRef.toPsiType(this, boxed = true) } + } + } + } + + is KtWhenConditionWithExpression -> + condition.expression?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) } + + else -> expr { UastEmptyExpression } + } + } + } + + internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression { + return expression?.let { convertExpression(it, parent, null) } ?: UastEmptyExpression + } + + internal fun convertOrNull(expression: KtExpression?, parent: UElement?): UExpression? { + return if (expression != null) convertExpression(expression, parent, null) else null + } + + internal fun KtPsiFactory.createAnalyzableExpression(text: String, context: PsiElement): KtExpression = + createAnalyzableProperty("val x = $text", context).initializer ?: error("Failed to create expression from text: '$text'") + + internal fun KtPsiFactory.createAnalyzableProperty(text: String, context: PsiElement): KtProperty = + createAnalyzableDeclaration(text, context) + + internal fun KtPsiFactory.createAnalyzableDeclaration(text: String, context: PsiElement): TDeclaration { + val file = createAnalyzableFile("dummy.kt", text, context) + val declarations = file.declarations + assert(declarations.size == 1) { "${declarations.size} declarations in $text" } + return declarations.first() as TDeclaration + } +} + +private fun convertVariablesDeclaration( + psi: KtVariableDeclaration, + parent: UElement? +): UDeclarationsExpression { + val declarationsExpression = KotlinUDeclarationsExpression(null, parent, psi) + val parentPsiElement = parent?.psi + val variable = KotlinUAnnotatedLocalVariable( + UastKotlinPsiVariable.create(psi, parentPsiElement, declarationsExpression), psi, declarationsExpression) { annotationParent -> + psi.annotationEntries.map { KotlinUAnnotation(it, annotationParent) } + } + return declarationsExpression.apply { declarations = listOf(variable) } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt index 486c8ecaa91..5b7f83dbda5 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt @@ -18,12 +18,13 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.declarations.KotlinUMethod abstract class KotlinUAnnotationBase( final override val psi: KtElement, givenParent: UElement? -) : KotlinAbstractUElement(givenParent), UAnnotation { +) : KotlinAbstractUElement(givenParent), UAnnotationEx, UAnchorOwner { abstract override val javaPsi: PsiAnnotation? @@ -115,10 +116,20 @@ class KotlinUAnnotation( override fun annotationUseSiteTarget() = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget() + override val uastAnchor by lazy { + KotlinUIdentifier( + javaPsi?.nameReferenceElement?.referenceNameElement, + annotationEntry.typeReference?.typeElement?.let { + (it as? KtUserType)?.referenceExpression?.getReferencedNameElement() ?: it.navigationElement + }, + this + ) + } + } class KotlinUNestedAnnotation( - original: KtCallExpression, + private val original: KtCallExpression, givenParent: UElement?, private val classDescriptor: ClassDescriptor? ) : KotlinUAnnotationBase(original, givenParent) { @@ -128,6 +139,14 @@ class KotlinUNestedAnnotation( override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null + override val uastAnchor by lazy { + KotlinUIdentifier( + javaPsi?.nameReferenceElement?.referenceNameElement, + (original.calleeExpression as? KtNameReferenceExpression)?.getReferencedNameElement(), + this + ) + } + } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.173 new file mode 100644 index 00000000000..486c8ecaa91 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.173 @@ -0,0 +1,133 @@ +package org.jetbrains.uast.kotlin + +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiClass +import org.jetbrains.kotlin.asJava.toLightAnnotation +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.name.FqNameUnsafe +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUMethod + +abstract class KotlinUAnnotationBase( + final override val psi: KtElement, + givenParent: UElement? +) : KotlinAbstractUElement(givenParent), UAnnotation { + + abstract override val javaPsi: PsiAnnotation? + + final override val sourcePsi = psi + + protected abstract fun annotationUseSiteTarget(): AnnotationUseSiteTarget? + + private val resolvedCall: ResolvedCall<*>? by lz { psi.getResolvedCall(psi.analyze()) } + + override val qualifiedName: String? + get() = annotationClassDescriptor.takeUnless(ErrorUtils::isError) + ?.fqNameUnsafe + ?.takeIf(FqNameUnsafe::isSafe) + ?.toSafe() + ?.toString() + + override val attributeValues: List by lz { + resolvedCall?.valueArguments?.entries?.mapNotNull { + val arguments = it.value.arguments + val name = it.key.name.asString() + when { + arguments.size == 1 -> + KotlinUNamedExpression.create(name, arguments.first(), this) + arguments.size > 1 -> + KotlinUNamedExpression.create(name, arguments, this) + else -> null + } + } ?: emptyList() + } + + protected abstract val annotationClassDescriptor: ClassDescriptor? + + override fun resolve(): PsiClass? { + val descriptor = annotationClassDescriptor ?: return null + return descriptor.toSource()?.getMaybeLightElement(this) as? PsiClass + } + + override fun findAttributeValue(name: String?): UExpression? = + findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value") + + fun findAttributeValueExpression(arg: ValueArgument): UExpression? { + val mapping = resolvedCall?.getArgumentMapping(arg) + return (mapping as? ArgumentMatch)?.let { match -> + val namedExpression = attributeValues.find { it.name == match.valueParameter.name.asString() } + namedExpression?.expression as? KotlinUVarargExpression ?: namedExpression + } + } + + override fun findDeclaredAttributeValue(name: String?): UExpression? { + return attributeValues.find { + it.name == name || + (name == null && it.name == "value") || + (name == "value" && it.name == null) + }?.expression + } + + private fun findAttributeDefaultValue(name: String): UExpression? { + val parameter = annotationClassDescriptor + ?.unsubstitutedPrimaryConstructor + ?.valueParameters + ?.find { it.name.asString() == name } ?: return null + + val defaultValue = (parameter.source.getPsi() as? KtParameter)?.defaultValue ?: return null + return getLanguagePlugin().convertWithParent(defaultValue) + } + + override fun convertParent(): UElement? { + val superParent = super.convertParent() ?: return null + if (annotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER) { + (superParent.uastParent as? KotlinUMethod)?.uastParameters?.firstIsInstance()?.let { + return it + } + } + return superParent + } +} + +class KotlinUAnnotation( + val annotationEntry: KtAnnotationEntry, + givenParent: UElement? +) : KotlinUAnnotationBase(annotationEntry, givenParent), UAnnotation { + + override val javaPsi = annotationEntry.toLightAnnotation() + + private val resolvedAnnotation: AnnotationDescriptor? by lz { annotationEntry.analyze()[BindingContext.ANNOTATION, annotationEntry] } + + override val annotationClassDescriptor: ClassDescriptor? + get() = resolvedAnnotation?.annotationClass + + override fun annotationUseSiteTarget() = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget() + +} + +class KotlinUNestedAnnotation( + original: KtCallExpression, + givenParent: UElement?, + private val classDescriptor: ClassDescriptor? +) : KotlinUAnnotationBase(original, givenParent) { + override val javaPsi: PsiAnnotation? by lazy { original.toLightAnnotation() } + override val annotationClassDescriptor: ClassDescriptor? + get() = classDescriptor + + override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null + +} + + diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt index 23634caebf8..886373290bd 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt @@ -26,10 +26,12 @@ import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.declarations.KotlinUMethod import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier -abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUElement(givenParent), UClass, JvmDeclarationUElement { +abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUElement(givenParent), UClassTypeSpecific, UAnchorOwner, + JvmDeclarationUElement { override val uastDeclarations by lz { mutableListOf().apply { @@ -48,9 +50,6 @@ abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUEle } } - override val uastAnchor: UElement? - get() = UIdentifier(psi.nameIdentifier, this) - override val annotations: List by lz { (sourcePsi as? KtModifierListOwner)?.annotationEntries.orEmpty().map { KotlinUAnnotation(it, this) } } @@ -81,8 +80,13 @@ open class KotlinUClass private constructor( override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile) - override val uastAnchor: UElement - get() = UIdentifier(nameIdentifier, this) + override val uastAnchor by lazy { getIdentifierSourcePsi()?.let { KotlinUIdentifier(nameIdentifier, it, this) } } + + private fun getIdentifierSourcePsi(): PsiElement? { + ktClass?.nameIdentifier?.let { return it } + (ktClass as? KtObjectDeclaration)?.getObjectKeyword()?.let { return it } + return null + } override fun getInnerClasses(): Array { // filter DefaultImpls to avoid processing same methods from original interface multiple times @@ -166,6 +170,14 @@ open class KotlinConstructorUMethod( } } + override val uastAnchor: KotlinUIdentifier by lazy { + KotlinUIdentifier( + psi.nameIdentifier, + if (isPrimary) ktClass?.nameIdentifier else (psi.kotlinOrigin as? KtSecondaryConstructor)?.getConstructorKeyword(), + this + ) + } + override val javaPsi = psi override val sourcePsi = psi.kotlinOrigin @@ -213,10 +225,9 @@ class KotlinUAnonymousClass( override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile) - override val uastAnchor: UElement? - get() { - val ktClassOrObject = (psi.originalElement as? KtLightClass)?.kotlinOrigin as? KtObjectDeclaration ?: return null - return UIdentifier(ktClassOrObject.getObjectKeyword(), this) + override val uastAnchor by lazy { + val ktClassOrObject = (psi.originalElement as? KtLightClass)?.kotlinOrigin as? KtObjectDeclaration ?: return@lazy null + KotlinUIdentifier(ktClassOrObject.getObjectKeyword(), this) } } @@ -229,8 +240,7 @@ class KotlinScriptUClass( override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, psi.kotlinOrigin) - override val uastAnchor: UElement - get() = UIdentifier(nameIdentifier, this) + override val uastAnchor by lazy { KotlinUIdentifier(nameIdentifier, sourcePsi?.nameIdentifier, this) } override val javaPsi: PsiClass = psi diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt.173 new file mode 100644 index 00000000000..23634caebf8 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt.173 @@ -0,0 +1,292 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.* +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUMethod +import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier + +abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUElement(givenParent), UClass, JvmDeclarationUElement { + + override val uastDeclarations by lz { + mutableListOf().apply { + addAll(fields) + addAll(initializers) + addAll(methods) + addAll(innerClasses) + } + } + + override val uastSuperTypes: List + get() { + val ktClass = (psi as? KtLightClass)?.kotlinOrigin ?: return emptyList() + return ktClass.superTypeListEntries.mapNotNull { it.typeReference }.map { + LazyKotlinUTypeReferenceExpression(it, this) + } + } + + override val uastAnchor: UElement? + get() = UIdentifier(psi.nameIdentifier, this) + + override val annotations: List by lz { + (sourcePsi as? KtModifierListOwner)?.annotationEntries.orEmpty().map { KotlinUAnnotation(it, this) } + } + + override fun equals(other: Any?) = other is AbstractKotlinUClass && psi == other.psi + override fun hashCode() = psi.hashCode() + +} + +open class KotlinUClass private constructor( + psi: KtLightClass, + givenParent: UElement? +) : AbstractKotlinUClass(givenParent), PsiClass by psi { + + val ktClass = psi.kotlinOrigin + + override val javaPsi: KtLightClass = psi + + override val sourcePsi: KtClassOrObject? = ktClass + + override val psi = unwrap(psi) + + override fun getSourceElement() = sourcePsi ?: this + + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() + + override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass) + + override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile) + + override val uastAnchor: UElement + get() = UIdentifier(nameIdentifier, this) + + override fun getInnerClasses(): Array { + // filter DefaultImpls to avoid processing same methods from original interface multiple times + // filter Enum entry classes to avoid duplication with PsiEnumConstant initializer class + return psi.innerClasses.filter { + it.name != JvmAbi.DEFAULT_IMPLS_CLASS_NAME && !it.isEnumEntryLightClass() + }.mapNotNull { + getLanguagePlugin().convertOpt(it, this) + }.toTypedArray() + } + + override fun getSuperClass(): UClass? = super.getSuperClass() + override fun getFields(): Array = super.getFields() + override fun getInitializers(): Array = super.getInitializers() + + override fun getMethods(): Array { + val hasPrimaryConstructor = ktClass?.hasPrimaryConstructor() ?: false + var secondaryConstructorsCount = 0 + + fun createUMethod(psiMethod: PsiMethod): UMethod { + return if (psiMethod is KtLightMethod && + psiMethod.isConstructor) { + if (!hasPrimaryConstructor && secondaryConstructorsCount++ == 0) + KotlinSecondaryConstructorWithInitializersUMethod(ktClass, psiMethod, this) + else + KotlinConstructorUMethod(ktClass, psiMethod, this) + } else { + getLanguagePlugin().convertOpt(psiMethod, this) ?: reportConvertFailure(psiMethod) + } + } + + fun isDelegatedMethod(psiMethod: PsiMethod) = psiMethod is KtLightMethod && psiMethod.isDelegated + + return psi.methods.asSequence() + .filterNot(::isDelegatedMethod) + .map(::createUMethod) + .toList() + .toTypedArray() + } + + private fun PsiClass.isEnumEntryLightClass() = (this as? KtLightClass)?.kotlinOrigin is KtEnumEntry + + companion object { + fun create(psi: KtLightClass, containingElement: UElement?): UClass = when (psi) { + is PsiAnonymousClass -> KotlinUAnonymousClass(psi, containingElement) + is KtLightClassForScript -> KotlinScriptUClass(psi, containingElement) + else -> KotlinUClass(psi, containingElement) + } + } + +} + +open class KotlinConstructorUMethod( + private val ktClass: KtClassOrObject?, + override val psi: KtLightMethod, + givenParent: UElement? +) : KotlinUMethod(psi, givenParent) { + + val isPrimary: Boolean + get() = psi.kotlinOrigin.let { it is KtPrimaryConstructor || it is KtClassOrObject } + + override val uastBody: UExpression? by lz { + val delegationCall: KtCallElement? = psi.kotlinOrigin.let { + when { + isPrimary -> ktClass?.superTypeListEntries?.firstIsInstanceOrNull() + it is KtSecondaryConstructor -> it.getDelegationCall() + else -> null + } + } + val bodyExpressions = getBodyExpressions() + if (delegationCall == null && bodyExpressions.isEmpty()) return@lz null + KotlinUBlockExpression.KotlinLazyUBlockExpression(this) { uastParent -> + SmartList().apply { + delegationCall?.let { + add(KotlinUFunctionCallExpression(it, uastParent)) + } + bodyExpressions.forEach { + add(KotlinConverter.convertOrEmpty(it, uastParent)) + } + } + } + } + + override val javaPsi = psi + + override val sourcePsi = psi.kotlinOrigin + + open protected fun getBodyExpressions(): List { + if (isPrimary) return getInitializers() + val bodyExpression = (psi.kotlinOrigin as? KtFunction)?.bodyExpression ?: return emptyList() + if (bodyExpression is KtBlockExpression) return bodyExpression.statements + return listOf(bodyExpression) + } + + protected fun getInitializers() = ktClass?.getAnonymousInitializers()?.mapNotNull { it.body } ?: emptyList() + +} + +// This class was created as a workaround for KT-21617 to be the only constructor which includes `init` block +// when there is no primary constructors in the class. +// It is expected to have only one constructor of this type in a UClass. +class KotlinSecondaryConstructorWithInitializersUMethod( + ktClass: KtClassOrObject?, + psi: KtLightMethod, + givenParent: UElement? +) : KotlinConstructorUMethod(ktClass, psi, givenParent) { + override fun getBodyExpressions(): List = getInitializers() + super.getBodyExpressions() +} + +class KotlinUAnonymousClass( + psi: PsiAnonymousClass, + givenParent: UElement? +) : AbstractKotlinUClass(givenParent), UAnonymousClass, PsiAnonymousClass by psi { + + override val psi: PsiAnonymousClass = unwrap(psi) + + override val javaPsi: PsiAnonymousClass = psi + + override val sourcePsi: KtClassOrObject? = (psi as? KtLightClass)?.kotlinOrigin + + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() + + override fun getSuperClass(): UClass? = super.getSuperClass() + override fun getFields(): Array = super.getFields() + override fun getMethods(): Array = super.getMethods() + override fun getInitializers(): Array = super.getInitializers() + override fun getInnerClasses(): Array = super.getInnerClasses() + + override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile) + + override val uastAnchor: UElement? + get() { + val ktClassOrObject = (psi.originalElement as? KtLightClass)?.kotlinOrigin as? KtObjectDeclaration ?: return null + return UIdentifier(ktClassOrObject.getObjectKeyword(), this) + } + +} + +class KotlinScriptUClass( + psi: KtLightClassForScript, + givenParent: UElement? +) : AbstractKotlinUClass(givenParent), PsiClass by psi { + override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile) + + override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, psi.kotlinOrigin) + + override val uastAnchor: UElement + get() = UIdentifier(nameIdentifier, this) + + override val javaPsi: PsiClass = psi + + override val sourcePsi: KtClassOrObject? = psi.kotlinOrigin + + override val psi = unwrap(psi) + + override fun getSuperClass(): UClass? = super.getSuperClass() + + override fun getFields(): Array = super.getFields() + + override fun getInitializers(): Array = super.getInitializers() + + override fun getInnerClasses(): Array = + psi.innerClasses.mapNotNull { getLanguagePlugin().convertOpt(it, this) }.toTypedArray() + + override fun getMethods(): Array = psi.methods.map(this::createUMethod).toTypedArray() + + private fun createUMethod(method: PsiMethod): UMethod { + return if (method.isConstructor) { + KotlinScriptConstructorUMethod(psi.script, method as KtLightMethod, this) + } + else { + getLanguagePlugin().convertOpt(method, this) ?: reportConvertFailure(method) + } + } + + override fun getOriginalElement(): PsiElement? = psi.originalElement + + class KotlinScriptConstructorUMethod( + script: KtScript, + override val psi: KtLightMethod, + givenParent: UElement? + ) : KotlinUMethod(psi, givenParent) { + override val uastBody: UExpression? by lz { + val initializers = script.declarations.filterIsInstance() + KotlinUBlockExpression.create(initializers, this) + } + override val javaPsi = psi + override val sourcePsi = psi.kotlinOrigin + } +} + +private fun reportConvertFailure(psiMethod: PsiMethod): Nothing { + val isValid = psiMethod.isValid + val report = KotlinExceptionWithAttachments( + "cant convert $psiMethod of ${psiMethod.javaClass} to UMethod" + + if (!isValid) " (method is not valid)" else "" + ) + + if (isValid) { + report.withAttachment("method", psiMethod.text) + psiMethod.containingFile?.let { + report.withAttachment("file", it.text) + } + } + + throw report +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt index 7d68de53c03..908675a061d 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt @@ -16,10 +16,7 @@ package org.jetbrains.uast.kotlin.declarations -import com.intellij.psi.PsiCodeBlock -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiMethod +import com.intellij.psi.* import org.jetbrains.kotlin.asJava.elements.KtLightElement import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.isGetter @@ -35,7 +32,7 @@ import org.jetbrains.uast.kotlin.* open class KotlinUMethod( psi: KtLightMethod, givenParent: UElement? -) : KotlinAbstractUElement(givenParent), UAnnotationMethod, JavaUElementWithComments, PsiMethod by psi { +) : KotlinAbstractUElement(givenParent), UAnnotationMethod, UMethodTypeSpecific, UAnchorOwner, JavaUElementWithComments, PsiMethod by psi { override val comments: List get() = super.comments @@ -78,8 +75,19 @@ open class KotlinUMethod( uParameters } - override val uastAnchor: UElement - get() = UIdentifier(nameIdentifier, this) + override val uastAnchor by lazy { + KotlinUIdentifier( + nameIdentifier, + sourcePsi.let { sourcePsi -> + when (sourcePsi) { + is PsiNameIdentifierOwner -> sourcePsi.nameIdentifier + is KtObjectDeclaration -> sourcePsi.getObjectKeyword() + else -> sourcePsi?.navigationElement + } + }, + this + ) + } override val uastBody by lz { @@ -110,9 +118,9 @@ open class KotlinUMethod( override val isOverride: Boolean get() = (kotlinOrigin as? KtCallableDeclaration)?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false - override fun getBody(): PsiCodeBlock? = super.getBody() + override fun getBody(): PsiCodeBlock? = super.getBody() - override fun getOriginalElement(): PsiElement? = super.getOriginalElement() + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() override fun equals(other: Any?) = other is KotlinUMethod && psi == other.psi diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.173 new file mode 100644 index 00000000000..7d68de53c03 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.173 @@ -0,0 +1,130 @@ +/* + * Copyright 2010-2016 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.uast.kotlin.declarations + +import com.intellij.psi.PsiCodeBlock +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.elements.isGetter +import org.jetbrains.kotlin.asJava.elements.isSetter +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.uast.* +import org.jetbrains.uast.java.internal.JavaUElementWithComments +import org.jetbrains.uast.kotlin.* + +open class KotlinUMethod( + psi: KtLightMethod, + givenParent: UElement? +) : KotlinAbstractUElement(givenParent), UAnnotationMethod, JavaUElementWithComments, PsiMethod by psi { + override val comments: List + get() = super.comments + + override val psi: KtLightMethod = unwrap(psi) + + override val javaPsi = psi + + override val sourcePsi = psi.kotlinOrigin + + override fun getSourceElement() = sourcePsi ?: this + + override val uastDefaultValue by lz { + val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null + val defaultValue = annotationParameter.defaultValue ?: return@lz null + getLanguagePlugin().convertElement(defaultValue, this) as? UExpression + } + + private val kotlinOrigin = (psi.originalElement as KtLightElement<*, *>).kotlinOrigin + + override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile) + + override fun getNameIdentifier() = UastLightIdentifier(psi, kotlinOrigin as KtNamedDeclaration?) + + override val annotations by lz { + psi.annotations + .mapNotNull { (it as? KtLightElement<*, *>)?.kotlinOrigin as? KtAnnotationEntry } + .map { KotlinUAnnotation(it, this) } + } + + private val receiver by lz { (sourcePsi as? KtCallableDeclaration)?.receiverTypeReference } + + override val uastParameters by lz { + val lightParams = psi.parameterList.parameters + val receiver = receiver ?: return@lz lightParams.map { + KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) + } + val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList() + val uParameters = SmartList(KotlinReceiverUParameter(receiverLight, receiver, this)) + lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) } + uParameters + } + + override val uastAnchor: UElement + get() = UIdentifier(nameIdentifier, this) + + + override val uastBody by lz { + val bodyExpression = when (kotlinOrigin) { + is KtFunction -> kotlinOrigin.bodyExpression + is KtProperty -> when { + psi.isGetter -> kotlinOrigin.getter?.bodyExpression + psi.isSetter -> kotlinOrigin.setter?.bodyExpression + else -> null + } + else -> null + } ?: return@lz null + + when (bodyExpression) { + !is KtBlockExpression -> { + KotlinUBlockExpression.KotlinLazyUBlockExpression(this, { block -> + val implicitReturn = KotlinUImplicitReturnExpression(block) + val uBody = getLanguagePlugin().convertElement(bodyExpression, implicitReturn) as? UExpression + ?: return@KotlinLazyUBlockExpression emptyList() + listOf(implicitReturn.apply { returnExpression = uBody }) + }) + + } + else -> getLanguagePlugin().convertElement(bodyExpression, this) as? UExpression + } + } + + override val isOverride: Boolean + get() = (kotlinOrigin as? KtCallableDeclaration)?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false + + override fun getBody(): PsiCodeBlock? = super.getBody() + + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() + + override fun equals(other: Any?) = other is KotlinUMethod && psi == other.psi + + companion object { + fun create(psi: KtLightMethod, containingElement: UElement?) = + if (psi.kotlinOrigin is KtConstructor<*>) { + KotlinConstructorUMethod( + psi.kotlinOrigin?.containingClassOrObject, + psi, containingElement + ) + } + else + KotlinUMethod(psi, containingElement) + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt index 6926a5e9353..a9402bd1226 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt @@ -32,14 +32,14 @@ import org.jetbrains.kotlin.types.typeUtil.nullability import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.uast.* import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier import org.jetbrains.uast.kotlin.internal.KotlinUElementWithComments import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable import org.jetbrains.uast.visitor.UastVisitor -abstract class AbstractKotlinUVariable(givenParent: UElement?) - : KotlinAbstractUElement(givenParent), PsiVariable, UVariable { +abstract class AbstractKotlinUVariable(givenParent: UElement?) : KotlinAbstractUElement(givenParent), PsiVariable, UVariable, UAnchorOwner { override val uastInitializer: UExpression? get() { @@ -94,12 +94,26 @@ abstract class AbstractKotlinUVariable(givenParent: UElement?) override val typeReference by lz { getLanguagePlugin().convertOpt(psi.typeElement, this) } - override val uastAnchor: UElement? - get() = UIdentifier(nameIdentifier, this) + override val uastAnchor: UIdentifier? + get() { + val sourcePsi = sourcePsi + val identifierSourcePsi = when (sourcePsi) { + is KtNamedDeclaration -> sourcePsi.nameIdentifier + is KtTypeReference -> sourcePsi.typeElement?.let { + // receiver param in extension function + (it as? KtUserType)?.referenceExpression?.getIdentifier() ?: it + } ?: sourcePsi + is KtBinaryExpression, is KtCallExpression -> null // e.g. `foo("Lorem ipsum") ?: foo("dolor sit amet")` + is KtDestructuringDeclaration -> sourcePsi.valOrVarKeyword + else -> sourcePsi + } ?: return null + return KotlinUIdentifier(nameIdentifier, identifierSourcePsi, this) + } override fun equals(other: Any?) = other is AbstractKotlinUVariable && psi == other.psi - class WrappedUAnnotation(psiAnnotation: PsiAnnotation, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement { + class WrappedUAnnotation(psiAnnotation: PsiAnnotation, override val uastParent: UElement) : UAnnotation, UAnchorOwner, + JvmDeclarationUElement { override val javaPsi: PsiAnnotation = psiAnnotation override val psi: PsiAnnotation = javaPsi @@ -109,6 +123,8 @@ abstract class AbstractKotlinUVariable(givenParent: UElement?) psi.parameterList.attributes.map { WrappedUNamedExpression(it, this) } } + override val uastAnchor by lazy { KotlinUIdentifier(javaPsi.nameReferenceElement?.referenceNameElement, null, this) } + class WrappedUNamedExpression(pair: PsiNameValuePair, override val uastParent: UElement?) : UNamedExpression, JvmDeclarationUElement { override val name: String? = pair.name override val psi = pair @@ -216,7 +232,8 @@ class KotlinReceiverUParameter( } -class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement { +class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotationEx, UAnchorOwner, + JvmDeclarationUElement { private fun getTargetType(annotatedElement: PsiElement): KotlinType? { if (annotatedElement is KtTypeReference) { @@ -235,6 +252,8 @@ class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override va return null } + override val uastAnchor: UIdentifier? = null + val nullability by lz { getTargetType(annotatedElement)?.nullability() } override val attributeValues: List @@ -360,7 +379,7 @@ class KotlinUEnumConstant( psi: PsiEnumConstant, override val sourcePsi: KtElement?, givenParent: UElement? -) : AbstractKotlinUVariable(givenParent), UEnumConstant, PsiEnumConstant by psi { +) : AbstractKotlinUVariable(givenParent), UEnumConstant, UCallExpressionEx, PsiEnumConstant by psi { override val initializingClass: UClass? by lz { (psi.initializingClass as? KtLightClass)?.let { initializingClass -> @@ -438,4 +457,7 @@ class KotlinUEnumConstant( override val identifier: String get() = psi.containingClass?.name ?: "" } + + override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i) + } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt.173 new file mode 100644 index 00000000000..6926a5e9353 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt.173 @@ -0,0 +1,441 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.TypeNullability +import org.jetbrains.kotlin.types.typeUtil.nullability +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.uast.* +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier +import org.jetbrains.uast.kotlin.internal.KotlinUElementWithComments +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable +import org.jetbrains.uast.visitor.UastVisitor + +abstract class AbstractKotlinUVariable(givenParent: UElement?) + : KotlinAbstractUElement(givenParent), PsiVariable, UVariable { + + override val uastInitializer: UExpression? + get() { + val psi = psi + val initializerExpression = when (psi) { + is UastKotlinPsiVariable -> psi.ktInitializer + is UastKotlinPsiParameter -> psi.ktDefaultValue + is KtLightElement<*, *> -> { + val origin = psi.kotlinOrigin + when (origin) { + is KtVariableDeclaration -> origin.initializer + is KtParameter -> origin.defaultValue + else -> null + } + } + else -> null + } ?: return null + return getLanguagePlugin().convertElement(initializerExpression, this) as? UExpression ?: UastEmptyExpression + } + + val delegateExpression: UExpression? by lz { + val psi = psi + val expression = when (psi) { + is KtLightElement<*, *> -> (psi.kotlinOrigin as? KtProperty)?.delegateExpression + is UastKotlinPsiVariable -> (psi.ktElement as? KtProperty)?.delegateExpression + else -> null + } + + expression?.let { getLanguagePlugin().convertElement(it, this) as? UExpression } + } + + override fun getNameIdentifier(): PsiIdentifier { + val kotlinOrigin = (psi as? KtLightElement<*, *>)?.kotlinOrigin + return UastLightIdentifier(psi, kotlinOrigin as KtNamedDeclaration?) + } + + override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile) + + override val annotations by lz { + val sourcePsi = sourcePsi ?: return@lz psi.annotations.map { WrappedUAnnotation(it, this) } + val annotations = SmartList(KotlinNullabilityUAnnotation(sourcePsi, this)) + if (sourcePsi is KtModifierListOwner) { + sourcePsi.annotationEntries. + filter { acceptsAnnotationTarget(it.useSiteTarget?.getAnnotationUseSiteTarget()) }. + mapTo(annotations) { KotlinUAnnotation(it, this) } + } + annotations + } + + + abstract protected fun acceptsAnnotationTarget(target: AnnotationUseSiteTarget?): Boolean + + override val typeReference by lz { getLanguagePlugin().convertOpt(psi.typeElement, this) } + + override val uastAnchor: UElement? + get() = UIdentifier(nameIdentifier, this) + + override fun equals(other: Any?) = other is AbstractKotlinUVariable && psi == other.psi + + class WrappedUAnnotation(psiAnnotation: PsiAnnotation, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement { + + override val javaPsi: PsiAnnotation = psiAnnotation + override val psi: PsiAnnotation = javaPsi + override val sourcePsi: PsiElement? = null + + override val attributeValues: List by lz { + psi.parameterList.attributes.map { WrappedUNamedExpression(it, this) } + } + + class WrappedUNamedExpression(pair: PsiNameValuePair, override val uastParent: UElement?) : UNamedExpression, JvmDeclarationUElement { + override val name: String? = pair.name + override val psi = pair + override val javaPsi: PsiElement? = psi + override val sourcePsi: PsiElement? = null + override val annotations: List = emptyList() + override val expression: UExpression by lz { toUExpression(psi.value) } + } + + override val qualifiedName: String? = psi.qualifiedName + override fun findAttributeValue(name: String?): UExpression? = psi.findAttributeValue(name)?.let { toUExpression(it) } + override fun findDeclaredAttributeValue(name: String?): UExpression? = psi.findDeclaredAttributeValue(name)?.let { toUExpression(it) } + override fun resolve(): PsiClass? = psi.nameReferenceElement?.resolve() as? PsiClass + } + +} + +private fun toUExpression(psi: PsiElement?): UExpression = psi.toUElementOfType() ?: UastEmptyExpression + +class KotlinUVariable( + psi: PsiVariable, + override val sourcePsi: KtElement, + givenParent: UElement? +) : AbstractKotlinUVariable(givenParent), UVariable, PsiVariable by psi { + + override val javaPsi = unwrap(psi) + + override val psi = javaPsi + + override val typeReference by lz { getLanguagePlugin().convertOpt(psi.typeElement, this) } + + override fun acceptsAnnotationTarget(target: AnnotationUseSiteTarget?): Boolean = true + + override fun getInitializer(): PsiExpression? { + return super.getInitializer() + } + + override fun getOriginalElement(): PsiElement? { + return super.getOriginalElement() + } + + override fun getNameIdentifier(): PsiIdentifier { + return super.getNameIdentifier() + } + + override fun getContainingFile(): PsiFile { + return super.getContainingFile() + } + +} + +open class KotlinUParameter( + psi: PsiParameter, + final override val sourcePsi: KtElement?, + givenParent: UElement? +) : AbstractKotlinUVariable(givenParent), UParameter, PsiParameter by psi { + + final override val javaPsi = unwrap(psi) + + override val psi = javaPsi + + private val isLightConstructorParam by lz { psi.getParentOfType(true)?.isConstructor } + + private val isKtConstructorParam by lz { sourcePsi?.getParentOfType(true)?.let { it is KtConstructor<*> } } + + override fun acceptsAnnotationTarget(target: AnnotationUseSiteTarget?): Boolean { + if (sourcePsi !is KtParameter) return false + if (isKtConstructorParam == isLightConstructorParam && target == null) return true + when (target) { + AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> return isLightConstructorParam == true + AnnotationUseSiteTarget.SETTER_PARAMETER -> return isLightConstructorParam != true + else -> return false + } + } + + override fun getInitializer(): PsiExpression? { + return super.getInitializer() + } + + override fun getOriginalElement(): PsiElement? { + return super.getOriginalElement() + } + + override fun getNameIdentifier(): PsiIdentifier { + return super.getNameIdentifier() + } + + override fun getContainingFile(): PsiFile { + return super.getContainingFile() + } +} + +class KotlinReceiverUParameter( + psi: PsiParameter, + private val receiver: KtTypeReference, + givenParent: UElement? +) : KotlinUParameter(psi, receiver, givenParent) { + + override val annotations: List by lz { + receiver.annotationEntries + .filter { it.useSiteTarget?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER } + .map { KotlinUAnnotation(it, this) } + + super.annotations + } + +} + +class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotation, JvmDeclarationUElement { + + private fun getTargetType(annotatedElement: PsiElement): KotlinType? { + if (annotatedElement is KtTypeReference) { + annotatedElement.getType()?.let { return it } + } + if (annotatedElement is KtCallableDeclaration) { + annotatedElement.typeReference?.getType()?.let { return it } + } + if (annotatedElement is KtProperty) { + annotatedElement.initializer?.let { it.getType(it.analyze()) }?.let { return it } + annotatedElement.delegateExpression?.let { it.getType(it.analyze())?.arguments?.firstOrNull()?.type }?.let { return it } + } + annotatedElement.getParentOfType(false)?.let { + it.typeReference?.getType() ?: it.initializer?.let { it.getType(it.analyze()) } + }?.let { return it } + return null + } + + val nullability by lz { getTargetType(annotatedElement)?.nullability() } + + override val attributeValues: List + get() = emptyList() + override val psi: PsiElement? + get() = null + override val javaPsi: PsiAnnotation? + get() = null + override val sourcePsi: PsiElement? + get() = null + override val qualifiedName: String? + get() = when (nullability) { + TypeNullability.NOT_NULL -> NotNull::class.qualifiedName + TypeNullability.NULLABLE -> Nullable::class.qualifiedName + TypeNullability.FLEXIBLE -> null + null -> null + } + + override fun findAttributeValue(name: String?): UExpression? = null + + override fun findDeclaredAttributeValue(name: String?): UExpression? = null + + override fun resolve(): PsiClass? = qualifiedName?.let { + val project = annotatedElement.project + JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.allScope(project)) + } + +} + +open class KotlinUField( + psi: PsiField, + override val sourcePsi: KtElement?, + givenParent: UElement? +) : AbstractKotlinUVariable(givenParent), UField, PsiField by psi { + override fun getSourceElement() = sourcePsi ?: this + + override val javaPsi = unwrap(psi) + + override val psi = javaPsi + + override fun acceptsAnnotationTarget(target: AnnotationUseSiteTarget?): Boolean = + target == AnnotationUseSiteTarget.FIELD || + target == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD || + (sourcePsi is KtProperty) && (target == null || target == AnnotationUseSiteTarget.PROPERTY) + + override fun getInitializer(): PsiExpression? { + return super.getInitializer() + } + + override fun getOriginalElement(): PsiElement? { + return super.getOriginalElement() + } + + override fun getNameIdentifier(): PsiIdentifier { + return super.getNameIdentifier() + } + + override fun getContainingFile(): PsiFile { + return super.getContainingFile() + } + + override fun isPhysical(): Boolean { + return true + } + + override fun accept(visitor: UastVisitor) { + if (visitor.visitField(this)) return + annotations.acceptList(visitor) + uastInitializer?.accept(visitor) + delegateExpression?.accept(visitor) + visitor.afterVisitField(this) + } +} + +open class KotlinULocalVariable( + psi: PsiLocalVariable, + override val sourcePsi: KtElement, + givenParent: UElement? +) : AbstractKotlinUVariable(givenParent), ULocalVariable, PsiLocalVariable by psi { + + override val javaPsi = unwrap(psi) + + override fun acceptsAnnotationTarget(target: AnnotationUseSiteTarget?): Boolean = true + + override val psi = javaPsi + + override fun getInitializer(): PsiExpression? { + return super.getInitializer() + } + + override fun getOriginalElement(): PsiElement? { + return super.getOriginalElement() + } + + override fun getNameIdentifier(): PsiIdentifier { + return super.getNameIdentifier() + } + + override fun getContainingFile(): PsiFile { + return super.getContainingFile() + } + + override fun accept(visitor: UastVisitor) { + if (visitor.visitLocalVariable(this)) return + annotations.acceptList(visitor) + uastInitializer?.accept(visitor) + delegateExpression?.accept(visitor) + visitor.afterVisitLocalVariable(this) + } +} + +open class KotlinUAnnotatedLocalVariable( + psi: PsiLocalVariable, + sourcePsi: KtElement, + uastParent: UElement?, + computeAnnotations: (parent: UElement) -> List +) : KotlinULocalVariable(psi, sourcePsi, uastParent) { + + override val annotations: List by lz { computeAnnotations(this) } +} + +class KotlinUEnumConstant( + psi: PsiEnumConstant, + override val sourcePsi: KtElement?, + givenParent: UElement? +) : AbstractKotlinUVariable(givenParent), UEnumConstant, PsiEnumConstant by psi { + + override val initializingClass: UClass? by lz { + (psi.initializingClass as? KtLightClass)?.let { initializingClass -> + KotlinUClass.create(initializingClass, this) + } + } + + override fun getInitializer(): PsiExpression? = super.getInitializer() + + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() + + override val javaPsi = unwrap(psi) + + override val psi = javaPsi + + override fun acceptsAnnotationTarget(target: AnnotationUseSiteTarget?): Boolean = true + + override fun getContainingFile(): PsiFile { + return super.getContainingFile() + } + + override fun getNameIdentifier(): PsiIdentifier { + return super.getNameIdentifier() + } + + override val kind: UastCallKind + get() = UastCallKind.CONSTRUCTOR_CALL + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null + + override val methodIdentifier: UIdentifier? + get() = null + + override val classReference: UReferenceExpression? + get() = KotlinEnumConstantClassReference(psi, sourcePsi, this) + + override val typeArgumentCount: Int + get() = 0 + + override val typeArguments: List + get() = emptyList() + + override val valueArgumentCount: Int + get() = psi.argumentList?.expressions?.size ?: 0 + + override val valueArguments by lz { + psi.argumentList?.expressions?.map { + getLanguagePlugin().convertElement(it, this) as? UExpression ?: UastEmptyExpression + } ?: emptyList() + } + + override val returnType: PsiType? + get() = psi.type + + override fun resolve() = psi.resolveMethod() + + override val methodName: String? + get() = null + + private class KotlinEnumConstantClassReference( + override val psi: PsiEnumConstant, + override val sourcePsi: KtElement?, + givenParent: UElement? + ) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression { + override val javaPsi: PsiElement? + get() = psi + + override fun resolve() = psi.containingClass + override val resolvedName: String? + get() = psi.containingClass?.name + override val identifier: String + get() = psi.containingClass?.name ?: "" + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt index 53f313d6cdf..2de1f41e4bf 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt @@ -16,13 +16,60 @@ package org.jetbrains.uast.kotlin.declarations +import com.intellij.openapi.application.ApplicationManager +import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiNameIdentifierOwner +import com.intellij.psi.impl.source.tree.LeafPsiElement +import com.intellij.psi.util.parents import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier -import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.* +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.kotlin.unwrapFakeFileForLightClass +import org.jetbrains.uast.toUElement -class UastLightIdentifier(lightOwner: PsiNameIdentifierOwner, ktDeclaration: KtNamedDeclaration?) - : KtLightIdentifier(lightOwner, ktDeclaration) { +class UastLightIdentifier(lightOwner: PsiNameIdentifierOwner, ktDeclaration: KtNamedDeclaration?) : + KtLightIdentifier(lightOwner, ktDeclaration) { override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(super.getContainingFile()) } + +class KotlinUIdentifier private constructor( + override val javaPsi: PsiElement?, + override val sourcePsi: PsiElement?, + override val psi: PsiElement?, + givenParent: UElement? +) : UIdentifier(psi, givenParent) { + + init { + if (ApplicationManager.getApplication().isUnitTestMode && !acceptableSourcePsi(sourcePsi)) + throw AssertionError("sourcePsi should be physical leaf element but got $sourcePsi of (${sourcePsi?.javaClass})") + } + + private fun acceptableSourcePsi(sourcePsi: PsiElement?): Boolean { + if (sourcePsi == null) return true + if (sourcePsi is LeafPsiElement) return true + if (sourcePsi is KtElement && sourcePsi.firstChild == null) return true + return false + } + + override val uastParent: UElement? by lazy { + if (givenParent != null) return@lazy givenParent + val parent = sourcePsi?.parent ?: return@lazy null + getIdentifierParentForCall(parent) ?: parent.toUElement() + } + + private fun getIdentifierParentForCall(parent: PsiElement): UElement? { + val parentParent = parent.parent + if (parentParent is KtCallElement && parentParent.calleeExpression == parent) { // method identifiers in calls + return parentParent.toUElement() + } + (parent.parents().take(3).find { it is KtTypeReference && it.parent is KtConstructorCalleeExpression })?.let { + return it.parent.parent.toUElement() + } + return null + } + + constructor(javaPsi: PsiElement?, sourcePsi: PsiElement?, uastParent: UElement?) : this(javaPsi, sourcePsi, javaPsi, uastParent) + constructor(sourcePsi: PsiElement?, uastParent: UElement?) : this(null, sourcePsi, sourcePsi, uastParent) +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt.173 new file mode 100644 index 00000000000..53f313d6cdf --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/UastLightIdentifier.kt.173 @@ -0,0 +1,28 @@ +/* + * 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.uast.kotlin.declarations + +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.uast.kotlin.unwrapFakeFileForLightClass + +class UastLightIdentifier(lightOwner: PsiNameIdentifierOwner, ktDeclaration: KtNamedDeclaration?) + : KtLightIdentifier(lightOwner, ktDeclaration) { + override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(super.getContainingFile()) +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt index 49ee7c994ca..e8b4f1706cd 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.CommonSupertypes import org.jetbrains.uast.* import org.jetbrains.uast.kotlin.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.kinds.KotlinSpecialExpressionKinds import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable @@ -42,7 +43,7 @@ private fun createNotEqWithNullExpression(variable: UVariable, containingElement override val leftOperand: UExpression by lz { createVariableReferenceExpression(variable, this) } override val rightOperand: UExpression by lz { createNullLiteralExpression(this) } override val operator: UastBinaryOperator = UastBinaryOperator.NOT_EQUALS - override val operatorIdentifier: UIdentifier? = UIdentifier(null, this) + override val operatorIdentifier: UIdentifier? = KotlinUIdentifier(null, this) override fun resolveOperator(): PsiMethod? = null override val annotations: List = emptyList() override val javaPsi: PsiElement? = null @@ -69,8 +70,8 @@ private fun createElvisExpressions( override val elseExpression: UExpression? by lz { KotlinConverter.convertExpression(right, this ) } override val isTernary: Boolean = false override val annotations: List = emptyList() - override val ifIdentifier: UIdentifier = UIdentifier(null, this) - override val elseIdentifier: UIdentifier? = UIdentifier(null, this) + override val ifIdentifier: UIdentifier = KotlinUIdentifier(null, this) + override val elseIdentifier: UIdentifier? = KotlinUIdentifier(null, this) } return listOf(declaration, ifExpression) diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.173 new file mode 100644 index 00000000000..49ee7c994ca --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/ElvisExpression.kt.173 @@ -0,0 +1,120 @@ +package org.jetbrains.uast.kotlin.expressions + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.CommonSupertypes +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.* +import org.jetbrains.uast.kotlin.kinds.KotlinSpecialExpressionKinds +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable + + +private fun createVariableReferenceExpression(variable: UVariable, containingElement: UElement?) = + object : USimpleNameReferenceExpression, JvmDeclarationUElement { + override val psi: PsiElement? = null + override fun resolve(): PsiElement? = variable + override val uastParent: UElement? = containingElement + override val resolvedName: String? = variable.name + override val annotations: List = emptyList() + override val identifier: String = variable.name.orAnonymous() + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + } + +private fun createNullLiteralExpression(containingElement: UElement?) = + object : ULiteralExpression, JvmDeclarationUElement { + override val psi: PsiElement? = null + override val uastParent: UElement? = containingElement + override val value: Any? = null + override val annotations: List = emptyList() + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + } + +private fun createNotEqWithNullExpression(variable: UVariable, containingElement: UElement?) = + object : UBinaryExpression, JvmDeclarationUElement { + override val psi: PsiElement? = null + override val uastParent: UElement? = containingElement + override val leftOperand: UExpression by lz { createVariableReferenceExpression(variable, this) } + override val rightOperand: UExpression by lz { createNullLiteralExpression(this) } + override val operator: UastBinaryOperator = UastBinaryOperator.NOT_EQUALS + override val operatorIdentifier: UIdentifier? = UIdentifier(null, this) + override fun resolveOperator(): PsiMethod? = null + override val annotations: List = emptyList() + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + } + +private fun createElvisExpressions( + left: KtExpression, + right: KtExpression, + containingElement: UElement?, + psiParent: PsiElement): List { + + val declaration = KotlinUDeclarationsExpression(containingElement) + val tempVariable = KotlinULocalVariable(UastKotlinPsiVariable.create(left, declaration, psiParent), left, declaration) + declaration.declarations = listOf(tempVariable) + + val ifExpression = object : UIfExpression, JvmDeclarationUElement { + override val psi: PsiElement? = null + override val uastParent: UElement? = containingElement + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + override val condition: UExpression by lz { createNotEqWithNullExpression(tempVariable, this) } + override val thenExpression: UExpression? by lz { createVariableReferenceExpression(tempVariable, this) } + override val elseExpression: UExpression? by lz { KotlinConverter.convertExpression(right, this ) } + override val isTernary: Boolean = false + override val annotations: List = emptyList() + override val ifIdentifier: UIdentifier = UIdentifier(null, this) + override val elseIdentifier: UIdentifier? = UIdentifier(null, this) + } + + return listOf(declaration, ifExpression) +} + +fun createElvisExpression(elvisExpression: KtBinaryExpression, givenParent: UElement?): UExpression { + val left = elvisExpression.left ?: return UastEmptyExpression + val right = elvisExpression.right ?: return UastEmptyExpression + + return KotlinUElvisExpression(elvisExpression, left, right, givenParent) +} + +class KotlinUElvisExpression( + private val elvisExpression: KtBinaryExpression, + private val left: KtExpression, + private val right: KtExpression, + givenParent: UElement? +) : KotlinAbstractUElement(givenParent), UExpressionList, KotlinEvaluatableUElement { + + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = elvisExpression + override val psi: PsiElement? = sourcePsi + override val kind = KotlinSpecialExpressionKinds.ELVIS + override val annotations: List = emptyList() + override val expressions: List by lz { + createElvisExpressions(left, right, this, elvisExpression.parent) + } + + val lhsDeclaration get() = (expressions[0] as UDeclarationsExpression).declarations.single() + val rhsIfExpression get() = expressions[1] as UIfExpression + + override fun asRenderString(): String { + return kind.name + " " + + expressions.joinToString(separator = "\n", prefix = "{\n", postfix = "\n}") { + it.asRenderString().withMargin + } + } + + override fun getExpressionType(): PsiType? { + val leftType = left.analyze()[BindingContext.EXPRESSION_TYPE_INFO, left]?.type ?: return null + val rightType = right.analyze()[BindingContext.EXPRESSION_TYPE_INFO, right]?.type ?: return null + + return CommonSupertypes + .commonSupertype(listOf(leftType, rightType)) + .toPsiType(this, elvisExpression, boxed = false) + } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt index 06c62b035ea..9aebf47d10d 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUBinaryExpression( override val psi: KtBinaryExpression, @@ -42,7 +43,7 @@ class KotlinUBinaryExpression( override val rightOperand by lz { KotlinConverter.convertOrEmpty(psi.right, this) } override val operatorIdentifier: UIdentifier? - get() = UIdentifier(psi.operationReference, this) + get() = KotlinUIdentifier(psi.operationReference, this) override fun resolveOperator() = psi.operationReference.resolveCallToDeclaration(context = this) as? PsiMethod diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt.173 new file mode 100644 index 00000000000..06c62b035ea --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBinaryExpression.kt.173 @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.uast.* + +class KotlinUBinaryExpression( + override val psi: KtBinaryExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UBinaryExpression, KotlinUElementWithType, KotlinEvaluatableUElement { + private companion object { + val BITWISE_OPERATORS = mapOf( + "or" to UastBinaryOperator.BITWISE_OR, + "and" to UastBinaryOperator.BITWISE_AND, + "xor" to UastBinaryOperator.BITWISE_XOR + ) + } + + override val leftOperand by lz { KotlinConverter.convertOrEmpty(psi.left, this) } + override val rightOperand by lz { KotlinConverter.convertOrEmpty(psi.right, this) } + + override val operatorIdentifier: UIdentifier? + get() = UIdentifier(psi.operationReference, this) + + override fun resolveOperator() = psi.operationReference.resolveCallToDeclaration(context = this) as? PsiMethod + + override val operator = when (psi.operationToken) { + KtTokens.EQ -> UastBinaryOperator.ASSIGN + KtTokens.PLUS -> UastBinaryOperator.PLUS + KtTokens.MINUS -> UastBinaryOperator.MINUS + KtTokens.MUL -> UastBinaryOperator.MULTIPLY + KtTokens.DIV -> UastBinaryOperator.DIV + KtTokens.PERC -> UastBinaryOperator.MOD + KtTokens.OROR -> UastBinaryOperator.LOGICAL_OR + KtTokens.ANDAND -> UastBinaryOperator.LOGICAL_AND + KtTokens.EQEQ -> UastBinaryOperator.EQUALS + KtTokens.EXCLEQ -> UastBinaryOperator.NOT_EQUALS + KtTokens.EQEQEQ -> UastBinaryOperator.IDENTITY_EQUALS + KtTokens.EXCLEQEQEQ -> UastBinaryOperator.IDENTITY_NOT_EQUALS + KtTokens.GT -> UastBinaryOperator.GREATER + KtTokens.GTEQ -> UastBinaryOperator.GREATER_OR_EQUALS + KtTokens.LT -> UastBinaryOperator.LESS + KtTokens.LTEQ -> UastBinaryOperator.LESS_OR_EQUALS + KtTokens.PLUSEQ -> UastBinaryOperator.PLUS_ASSIGN + KtTokens.MINUSEQ -> UastBinaryOperator.MINUS_ASSIGN + KtTokens.MULTEQ -> UastBinaryOperator.MULTIPLY_ASSIGN + KtTokens.DIVEQ -> UastBinaryOperator.DIVIDE_ASSIGN + KtTokens.PERCEQ -> UastBinaryOperator.REMAINDER_ASSIGN + KtTokens.IN_KEYWORD -> KotlinBinaryOperators.IN + KtTokens.NOT_IN -> KotlinBinaryOperators.NOT_IN + KtTokens.RANGE -> KotlinBinaryOperators.RANGE_TO + else -> run { // Handle bitwise operators + val other = UastBinaryOperator.OTHER + val ref = psi.operationReference + val resolvedCall = psi.operationReference.getResolvedCall(ref.analyze()) ?: return@run other + val resultingDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return@run other + val applicableOperator = BITWISE_OPERATORS[resultingDescriptor.name.asString()] ?: return@run other + + val containingClass = resultingDescriptor.containingDeclaration as? ClassDescriptor ?: return@run other + if (containingClass.typeConstructor.supertypes.any { + it.constructor.declarationDescriptor?.fqNameSafe?.asString() == "kotlin.Number" + }) applicableOperator else other + } + } +} + +class KotlinCustomUBinaryExpression( + override val psi: PsiElement, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UBinaryExpression { + lateinit override var leftOperand: UExpression + internal set + + lateinit override var operator: UastBinaryOperator + internal set + + lateinit override var rightOperand: UExpression + internal set + + override val operatorIdentifier: UIdentifier? + get() = null + + override fun resolveOperator() = null +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt index 69ec94ebbdb..f0d59284ab6 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt @@ -18,7 +18,7 @@ import org.jetbrains.uast.kotlin.KotlinUElementWithType class KotlinUCollectionLiteralExpression( override val sourcePsi: KtCollectionLiteralExpression, givenParent: UElement? -) : KotlinAbstractUExpression(givenParent), UCallExpression, KotlinUElementWithType { +) : KotlinAbstractUExpression(givenParent), UCallExpressionEx, KotlinUElementWithType { override val classReference: UReferenceExpression? get() = null @@ -51,4 +51,6 @@ class KotlinUCollectionLiteralExpression( override val psi: PsiElement get() = sourcePsi + override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i) + } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt.173 new file mode 100644 index 00000000000..69ec94ebbdb --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt.173 @@ -0,0 +1,54 @@ +/* + * 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.uast.kotlin.expressions + +import com.intellij.psi.PsiArrayType +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.KotlinAbstractUExpression +import org.jetbrains.uast.kotlin.KotlinConverter +import org.jetbrains.uast.kotlin.KotlinUElementWithType + +class KotlinUCollectionLiteralExpression( + override val sourcePsi: KtCollectionLiteralExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UCallExpression, KotlinUElementWithType { + + override val classReference: UReferenceExpression? get() = null + + override val kind: UastCallKind = UastCallKind.NESTED_ARRAY_INITIALIZER + + override val methodIdentifier: UIdentifier? by lazy { UIdentifier(sourcePsi.leftBracket, this) } + + override val methodName: String? get() = null + + override val receiver: UExpression? get() = null + + override val receiverType: PsiType? get() = null + + override val returnType: PsiType? get() = getExpressionType() + + override val typeArgumentCount: Int get() = typeArguments.size + + override val typeArguments: List get() = listOfNotNull((returnType as? PsiArrayType)?.componentType) + + override val valueArgumentCount: Int + get() = sourcePsi.getInnerExpressions().size + + override val valueArguments by lazy { + sourcePsi.getInnerExpressions().map { KotlinConverter.convertOrEmpty(it, this) } + } + + override fun asRenderString(): String = "collectionLiteral[" + valueArguments.joinToString { it.asRenderString() } + "]" + + override fun resolve(): PsiMethod? = null + + override val psi: PsiElement get() = sourcePsi + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt index 8461fad28e8..b79fd597717 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.psi.KtDoWhileExpression import org.jetbrains.uast.UDoWhileExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUDoWhileExpression( override val psi: KtDoWhileExpression, @@ -29,8 +30,8 @@ class KotlinUDoWhileExpression( override val body by lz { KotlinConverter.convertOrEmpty(psi.body, this) } override val doIdentifier: UIdentifier - get() = UIdentifier(null, this) + get() = KotlinUIdentifier(null, this) override val whileIdentifier: UIdentifier - get() = UIdentifier(null, this) + get() = KotlinUIdentifier(null, this) } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt.173 new file mode 100644 index 00000000000..8461fad28e8 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUDoWhileExpression.kt.173 @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.psi.KtDoWhileExpression +import org.jetbrains.uast.UDoWhileExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier + +class KotlinUDoWhileExpression( + override val psi: KtDoWhileExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UDoWhileExpression { + override val condition by lz { KotlinConverter.convertOrEmpty(psi.condition, this) } + override val body by lz { KotlinConverter.convertOrEmpty(psi.body, this) } + + override val doIdentifier: UIdentifier + get() = UIdentifier(null, this) + + override val whileIdentifier: UIdentifier + get() = UIdentifier(null, this) +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt index 7345cbe99d7..4d6c8f2c6da 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UForEachExpression import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter import org.jetbrains.uast.psi.UastPsiParameterNotResolved @@ -38,5 +39,5 @@ class KotlinUForEachExpression( } override val forIdentifier: UIdentifier - get() = UIdentifier(null, this) + get() = KotlinUIdentifier(null, this) } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt.173 new file mode 100644 index 00000000000..7345cbe99d7 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUForEachExpression.kt.173 @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UForEachExpression +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter +import org.jetbrains.uast.psi.UastPsiParameterNotResolved + +class KotlinUForEachExpression( + override val psi: KtForExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UForEachExpression { + override val iteratedValue by lz { KotlinConverter.convertOrEmpty(psi.loopRange, this) } + override val body by lz { KotlinConverter.convertOrEmpty(psi.body, this) } + + override val variable by lz { + val parameter = psi.loopParameter?.let { UastKotlinPsiParameter.create(it, psi, this, 0) } + ?: UastPsiParameterNotResolved(psi, KotlinLanguage.INSTANCE) + KotlinUParameter(parameter, psi, this) + } + + override val forIdentifier: UIdentifier + get() = UIdentifier(null, this) +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt index b2dfc81fcab..db86b21c68b 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt @@ -21,25 +21,24 @@ import com.intellij.psi.PsiMethod import com.intellij.psi.PsiType import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtCallElement -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.uast.* import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.visitor.UastVisitor class KotlinUFunctionCallExpression( override val psi: KtCallElement, givenParent: UElement?, private val _resolvedCall: ResolvedCall<*>? -) : KotlinAbstractUExpression(givenParent), UCallExpression, KotlinUElementWithType { +) : KotlinAbstractUExpression(givenParent), UCallExpressionEx, KotlinUElementWithType { companion object { fun resolveSource(descriptor: DeclarationDescriptor, source: PsiElement?): PsiMethod? { if (descriptor is ConstructorDescriptor && descriptor.isPrimary @@ -75,8 +74,19 @@ class KotlinUFunctionCallExpression( } override val methodIdentifier by lz { - val calleeExpression = psi.calleeExpression ?: return@lz null - UIdentifier(calleeExpression, this) + val calleeExpression = psi.calleeExpression + when (calleeExpression) { + null -> null + is KtNameReferenceExpression -> + KotlinUIdentifier(calleeExpression.getReferencedNameElement(), this) + is KtConstructorDelegationReferenceExpression -> + KotlinUIdentifier(calleeExpression.firstChild ?: calleeExpression, this) + is KtConstructorCalleeExpression -> + KotlinUIdentifier( + calleeExpression.constructorReferenceExpression?.getReferencedNameElement() ?: calleeExpression, this + ) + else -> KotlinUIdentifier(calleeExpression, this) + } } override val valueArgumentCount: Int @@ -84,6 +94,14 @@ class KotlinUFunctionCallExpression( override val valueArguments by lz { psi.valueArguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), this) } } + override fun getArgumentForParameter(i: Int): UExpression? { + val resolvedCall = resolvedCall ?: return null + val actualParamIndex = if (resolvedCall.extensionReceiver == null) i else i - 1 + if (actualParamIndex == -1) return receiver + return getArgumentExpressionByIndex(actualParamIndex, resolvedCall, this) + } + + override val typeArgumentCount: Int get() = psi.typeArguments.size @@ -138,4 +156,28 @@ class KotlinUFunctionCallExpression( } } -} \ No newline at end of file +} + +internal fun getArgumentExpressionByIndex( + actualParamIndex: Int, + resolvedCall: ResolvedCall, + parent: UElement +): UExpression? { + val (parameter, resolvedArgument) = resolvedCall.valueArguments.entries.find { it.key.index == actualParamIndex } ?: return null + val arguments = resolvedArgument.arguments + if (arguments.isEmpty()) return null + if (arguments.size == 1) { + val argument = arguments.single() + val expression = argument.getArgumentExpression() + if (parameter.varargElementType != null && argument.getSpreadElement() == null) { + return createVarargsHolder(arguments, parent) + } + return KotlinConverter.convertOrEmpty(expression, parent) + } + return createVarargsHolder(arguments, parent) +} + +private fun createVarargsHolder(arguments: List, parent: UElement?): KotlinUExpressionList = + KotlinUExpressionList(null, UastSpecialExpressionKind.VARARGS, parent).apply { + expressions = arguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), parent) } + } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 new file mode 100644 index 00000000000..b2dfc81fcab --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.173 @@ -0,0 +1,141 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import org.jetbrains.kotlin.asJava.LightClassUtil +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtCallElement +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.uast.* +import org.jetbrains.uast.internal.acceptList +import org.jetbrains.uast.visitor.UastVisitor + +class KotlinUFunctionCallExpression( + override val psi: KtCallElement, + givenParent: UElement?, + private val _resolvedCall: ResolvedCall<*>? +) : KotlinAbstractUExpression(givenParent), UCallExpression, KotlinUElementWithType { + companion object { + fun resolveSource(descriptor: DeclarationDescriptor, source: PsiElement?): PsiMethod? { + if (descriptor is ConstructorDescriptor && descriptor.isPrimary + && source is KtClassOrObject && source.primaryConstructor == null + && source.secondaryConstructors.isEmpty()) { + return source.toLightClass()?.constructors?.firstOrNull() + } + + return when (source) { + is KtFunction -> LightClassUtil.getLightClassMethod(source) + is PsiMethod -> source + else -> null + } + } + } + + constructor(psi: KtCallElement, uastParent: UElement?) : this(psi, uastParent, null) + + private val resolvedCall by lz { + _resolvedCall ?: psi.getResolvedCall(psi.analyze()) + } + + override val receiverType by lz { + val resolvedCall = this.resolvedCall ?: return@lz null + val receiver = resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver ?: return@lz null + receiver.type.toPsiType(this, psi, boxed = true) + } + + override val methodName by lz { resolvedCall?.resultingDescriptor?.name?.asString() } + + override val classReference by lz { + KotlinClassViaConstructorUSimpleReferenceExpression(psi, methodName.orAnonymous("class"), this) + } + + override val methodIdentifier by lz { + val calleeExpression = psi.calleeExpression ?: return@lz null + UIdentifier(calleeExpression, this) + } + + override val valueArgumentCount: Int + get() = psi.valueArguments.size + + override val valueArguments by lz { psi.valueArguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), this) } } + + override val typeArgumentCount: Int + get() = psi.typeArguments.size + + override val typeArguments by lz { psi.typeArguments.map { it.typeReference.toPsiType(this, boxed = true) } } + + override val returnType: PsiType? + get() = getExpressionType() + + override val kind: UastCallKind by lz { + val resolvedCall = resolvedCall ?: return@lz UastCallKind.METHOD_CALL + when { + resolvedCall.resultingDescriptor is ConstructorDescriptor -> UastCallKind.CONSTRUCTOR_CALL + this.isAnnotationArgumentArrayInitializer() -> UastCallKind.NESTED_ARRAY_INITIALIZER + else -> UastCallKind.METHOD_CALL + } + } + + override val receiver: UExpression? + get() = (uastParent as? UQualifiedReferenceExpression)?.takeIf { it.selector == this }?.receiver + + override fun resolve(): PsiMethod? { + val descriptor = resolvedCall?.resultingDescriptor ?: return null + val source = descriptor.toSource() ?: return null + return resolveSource(descriptor, source) + } + + override fun accept(visitor: UastVisitor) { + if (visitor.visitCallExpression(this)) return + methodIdentifier?.accept(visitor) + classReference.accept(visitor) + valueArguments.acceptList(visitor) + + visitor.afterVisitCallExpression(this) + } + + private fun isAnnotationArgumentArrayInitializer(): Boolean { + val resolvedCall = resolvedCall ?: return false + // KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call + return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) + } + + override fun convertParent(): UElement? = super.convertParent().let { result -> + when (result) { + is UMethod -> result.uastBody ?: result + is UClass -> + result.methods + .filterIsInstance() + .firstOrNull { it.isPrimary } + ?.uastBody + ?: result + else -> result + } + } + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt index 2130def4799..e376f40d537 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.psi.KtLabeledExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.ULabeledExpression +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinULabeledExpression( override val psi: KtLabeledExpression, @@ -29,7 +30,7 @@ class KotlinULabeledExpression( get() = psi.getLabelName().orAnonymous("label") override val labelIdentifier: UIdentifier? - get() = psi.getTargetLabel()?.let { UIdentifier(it, this) } + get() = psi.getTargetLabel()?.let { KotlinUIdentifier(it, this) } override val expression by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt.173 new file mode 100644 index 00000000000..2130def4799 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinULabeledExpression.kt.173 @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.psi.KtLabeledExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.ULabeledExpression + +class KotlinULabeledExpression( + override val psi: KtLabeledExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), ULabeledExpression { + override val label: String + get() = psi.getLabelName().orAnonymous("label") + + override val labelIdentifier: UIdentifier? + get() = psi.getTargetLabel()?.let { UIdentifier(it, this) } + + override val expression by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt index 21c1459699f..273bdf3b6cb 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt @@ -55,7 +55,8 @@ class KotlinUNamedExpression private constructor( } } class KotlinUVarargExpression(private val valueArgs: List, - uastParent: UElement?) : KotlinAbstractUExpression(uastParent), UCallExpression { + uastParent: UElement? +) : KotlinAbstractUExpression(uastParent), UCallExpressionEx { override val kind: UastCallKind = UastCallKind.NESTED_ARRAY_INITIALIZER override val valueArguments: List by lz { @@ -66,6 +67,8 @@ class KotlinUVarargExpression(private val valueArgs: List, } } + override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i) + override val valueArgumentCount: Int get() = valueArgs.size diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt.173 new file mode 100644 index 00000000000..21c1459699f --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt.173 @@ -0,0 +1,100 @@ +/* + * 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.uast.kotlin + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.uast.* + +class KotlinUNamedExpression private constructor( + override val name: String?, + override val sourcePsi: PsiElement?, + givenParent: UElement?, + expressionProducer: (UElement) -> UExpression +) : KotlinAbstractUElement(givenParent), UNamedExpression { + + override val expression: UExpression by lz { expressionProducer(this) } + + override val annotations: List = emptyList() + + override val psi: PsiElement? = null + + override val javaPsi: PsiElement? = null + + companion object { + internal fun create(name: String?, valueArgument: ValueArgument, uastParent: UElement?): UNamedExpression { + val expression = valueArgument.getArgumentExpression() + return KotlinUNamedExpression(name, valueArgument.asElement(), uastParent) { expressionParent -> + expression?.let { expressionParent.getLanguagePlugin().convert(it, expressionParent) } ?: UastEmptyExpression + } + } + + internal fun create( + name: String?, + valueArguments: List, + uastParent: UElement?): UNamedExpression { + return KotlinUNamedExpression(name, null, uastParent) { expressionParent -> + KotlinUVarargExpression(valueArguments, expressionParent) + } + } + } +} +class KotlinUVarargExpression(private val valueArgs: List, + uastParent: UElement?) : KotlinAbstractUExpression(uastParent), UCallExpression { + override val kind: UastCallKind = UastCallKind.NESTED_ARRAY_INITIALIZER + + override val valueArguments: List by lz { + valueArgs.map { + it.getArgumentExpression()?.let { argumentExpression -> + getLanguagePlugin().convert(argumentExpression, this) + } ?: UastEmptyExpression + } + } + + override val valueArgumentCount: Int + get() = valueArgs.size + + override val psi: PsiElement? + get() = null + + override val methodIdentifier: UIdentifier? + get() = null + + override val classReference: UReferenceExpression? + get() = null + + override val methodName: String? + get() = null + + override val typeArgumentCount: Int + get() = 0 + + override val typeArguments: List + get() = emptyList() + + override val returnType: PsiType? + get() = null + + override fun resolve() = null + + override val receiver: UExpression? + get() = null + + override val receiverType: PsiType? + get() = null +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt index b65009c9809..b4b70818cd3 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt @@ -25,12 +25,14 @@ import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.psi.KtObjectLiteralExpression import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.uast.* class KotlinUObjectLiteralExpression( override val psi: KtObjectLiteralExpression, givenParent: UElement? -) : KotlinAbstractUExpression(givenParent), UObjectLiteralExpression, KotlinUElementWithType { +) : KotlinAbstractUExpression(givenParent), UObjectLiteralExpression, UCallExpressionEx, KotlinUElementWithType { + override val declaration: UClass by lz { val lightClass: KtLightClass? = psi.objectDeclaration.toLightClass() if (lightClass != null) { @@ -70,7 +72,10 @@ class KotlinUObjectLiteralExpression( } override fun resolve() = superClassConstructorCall?.resolveCallToDeclaration(this) as? PsiMethod - + + override fun getArgumentForParameter(i: Int): UExpression? = + superClassConstructorCall?.let { it.getResolvedCall(it.analyze()) }?.let { getArgumentExpressionByIndex(i, it, this) } + private class ObjectLiteralClassReference( override val psi: KtSuperTypeCallEntry, givenParent: UElement? diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt.173 new file mode 100644 index 00000000000..b65009c9809 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt.173 @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.openapi.diagnostic.Attachment +import com.intellij.openapi.diagnostic.Logger +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiType +import com.intellij.psi.impl.light.LightPsiClassBuilder +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.psi.KtObjectLiteralExpression +import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry +import org.jetbrains.uast.* + +class KotlinUObjectLiteralExpression( + override val psi: KtObjectLiteralExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UObjectLiteralExpression, KotlinUElementWithType { + override val declaration: UClass by lz { + val lightClass: KtLightClass? = psi.objectDeclaration.toLightClass() + if (lightClass != null) { + getLanguagePlugin().convert(lightClass, this) + } + else { + logger.error( + "Failed to create light class for object declaration", + Attachment(psi.containingFile.virtualFile?.path ?: "", psi.containingFile.text)) + + getLanguagePlugin().convert(LightPsiClassBuilder(psi, ""), this) + } + } + + override fun getExpressionType() = psi.objectDeclaration.toPsiType() + + private val superClassConstructorCall by lz { + psi.objectDeclaration.superTypeListEntries.firstOrNull { it is KtSuperTypeCallEntry } as? KtSuperTypeCallEntry + } + + override val classReference: UReferenceExpression? by lz { superClassConstructorCall?.let { ObjectLiteralClassReference(it, this) } } + + override val valueArgumentCount: Int + get() = superClassConstructorCall?.valueArguments?.size ?: 0 + + override val valueArguments by lz { + val psi = superClassConstructorCall ?: return@lz emptyList() + psi.valueArguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), this) } + } + + override val typeArgumentCount: Int + get() = superClassConstructorCall?.typeArguments?.size ?: 0 + + override val typeArguments by lz { + val psi = superClassConstructorCall ?: return@lz emptyList() + psi.typeArguments.map { it.typeReference.toPsiType(this, boxed = true) } + } + + override fun resolve() = superClassConstructorCall?.resolveCallToDeclaration(this) as? PsiMethod + + private class ObjectLiteralClassReference( + override val psi: KtSuperTypeCallEntry, + givenParent: UElement? + ) : KotlinAbstractUElement(givenParent), USimpleNameReferenceExpression { + + override val javaPsi = null + override val sourcePsi = psi + + override fun resolve() = (psi.resolveCallToDeclaration(this) as? PsiMethod)?.containingClass + + override val annotations: List + get() = emptyList() + + override val resolvedName: String? + get() = identifier + + override val identifier: String + get() = psi.name ?: "" + } + + companion object { + val logger by lz { Logger.getInstance(KotlinUObjectLiteralExpression::class.java) } + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt index 5e6963bb4fc..4d86d3b6bbf 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt @@ -20,6 +20,7 @@ import com.intellij.psi.PsiMethod import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtPostfixExpression import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUPostfixExpression( override val psi: KtPostfixExpression, @@ -35,7 +36,7 @@ class KotlinUPostfixExpression( } override val operatorIdentifier: UIdentifier? - get() = UIdentifier(psi.operationReference, this) + get() = KotlinUIdentifier(psi.operationReference, this) override fun resolveOperator() = psi.operationReference.resolveCallToDeclaration(context = this) as? PsiMethod diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt.173 new file mode 100644 index 00000000000..5e6963bb4fc --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt.173 @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtPostfixExpression +import org.jetbrains.uast.* + +class KotlinUPostfixExpression( + override val psi: KtPostfixExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UPostfixExpression, KotlinUElementWithType, KotlinEvaluatableUElement, UResolvable { + override val operand by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } + + override val operator = when (psi.operationToken) { + KtTokens.PLUSPLUS -> UastPostfixOperator.INC + KtTokens.MINUSMINUS -> UastPostfixOperator.DEC + KtTokens.EXCLEXCL -> KotlinPostfixOperators.EXCLEXCL + else -> UastPostfixOperator.UNKNOWN + } + + override val operatorIdentifier: UIdentifier? + get() = UIdentifier(psi.operationReference, this) + + override fun resolveOperator() = psi.operationReference.resolveCallToDeclaration(context = this) as? PsiMethod + + override fun resolve(): PsiMethod? = when (psi.operationToken) { + KtTokens.EXCLEXCL -> operand.tryResolve() as? PsiMethod + else -> null + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt index af7d2f999d3..63c9af6d50b 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt @@ -23,6 +23,7 @@ import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.UPrefixExpression import org.jetbrains.uast.UastPrefixOperator +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUPrefixExpression( override val psi: KtPrefixExpression, @@ -31,7 +32,7 @@ class KotlinUPrefixExpression( override val operand by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } override val operatorIdentifier: UIdentifier? - get() = UIdentifier(psi.operationReference, this) + get() = KotlinUIdentifier(psi.operationReference, this) override fun resolveOperator() = psi.operationReference.resolveCallToDeclaration(context = this) as? PsiMethod diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt.173 new file mode 100644 index 00000000000..af7d2f999d3 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPrefixExpression.kt.173 @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtPrefixExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UPrefixExpression +import org.jetbrains.uast.UastPrefixOperator + +class KotlinUPrefixExpression( + override val psi: KtPrefixExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UPrefixExpression, KotlinUElementWithType, KotlinEvaluatableUElement { + override val operand by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } + + override val operatorIdentifier: UIdentifier? + get() = UIdentifier(psi.operationReference, this) + + override fun resolveOperator() = psi.operationReference.resolveCallToDeclaration(context = this) as? PsiMethod + + override val operator = when (psi.operationToken) { + KtTokens.EXCL -> UastPrefixOperator.LOGICAL_NOT + KtTokens.PLUS -> UastPrefixOperator.UNARY_PLUS + KtTokens.MINUS -> UastPrefixOperator.UNARY_MINUS + KtTokens.PLUSPLUS -> UastPrefixOperator.INC + KtTokens.MINUSMINUS -> UastPrefixOperator.DEC + else -> UastPrefixOperator.UNKNOWN + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt index e3f258a99e6..3390745f886 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.utils.addToStdlib.constant import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.visitor.UastVisitor open class KotlinUSimpleReferenceExpression( @@ -96,12 +97,12 @@ open class KotlinUSimpleReferenceExpression( } class KotlinAccessorCallExpression( - override val psi: KtElement, - override val uastParent: KotlinUSimpleReferenceExpression, - private val resolvedCall: ResolvedCall<*>, - private val accessorDescriptor: DeclarationDescriptor, - val setterValue: KtExpression? - ) : UCallExpression, JvmDeclarationUElement { + override val psi: KtSimpleNameExpression, + override val uastParent: KotlinUSimpleReferenceExpression, + private val resolvedCall: ResolvedCall<*>, + private val accessorDescriptor: DeclarationDescriptor, + val setterValue: KtExpression? + ) : UCallExpressionEx, JvmDeclarationUElement { override val methodName: String? get() = accessorDescriptor.name.asString() @@ -125,8 +126,7 @@ open class KotlinUSimpleReferenceExpression( type.toPsiType(this, psi, boxed = true) } - override val methodIdentifier: UIdentifier? - get() = UIdentifier(uastParent.psi, this) + override val methodIdentifier: UIdentifier? by lazy { KotlinUIdentifier(psi.getReferencedNameElement(), this) } override val classReference: UReferenceExpression? get() = null @@ -141,6 +141,8 @@ open class KotlinUSimpleReferenceExpression( emptyList() } + override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i) + override val typeArgumentCount: Int get() = resolvedCall.typeArguments.size diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt.173 new file mode 100644 index 00000000000..e3f258a99e6 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt.173 @@ -0,0 +1,220 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiNamedElement +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor +import org.jetbrains.kotlin.utils.addToStdlib.constant +import org.jetbrains.uast.* +import org.jetbrains.uast.visitor.UastVisitor + +open class KotlinUSimpleReferenceExpression( + override val psi: KtSimpleNameExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression, KotlinUElementWithType, KotlinEvaluatableUElement { + private val resolvedDeclaration by lz { psi.resolveCallToDeclaration(this) } + + override val identifier get() = psi.getReferencedName() + + override fun resolve() = resolvedDeclaration + + override val resolvedName: String? + get() = (resolvedDeclaration as? PsiNamedElement)?.name + + override fun accept(visitor: UastVisitor) { + visitor.visitSimpleNameReferenceExpression(this) + + if (psi.parent.destructuringDeclarationInitializer != true) { + visitAccessorCalls(visitor) + } + + visitor.afterVisitSimpleNameReferenceExpression(this) + } + + private fun visitAccessorCalls(visitor: UastVisitor) { + // Visit Kotlin get-set synthetic Java property calls as function calls + val bindingContext = psi.analyze() + val access = psi.readWriteAccess() + val resolvedCall = psi.getResolvedCall(bindingContext) + val resultingDescriptor = resolvedCall?.resultingDescriptor as? SyntheticJavaPropertyDescriptor + if (resultingDescriptor != null) { + val setterValue = if (access.isWrite) { + findAssignment(psi, psi.parent)?.right ?: run { + visitor.afterVisitSimpleNameReferenceExpression(this) + return + } + } else { + null + } + + if (resolvedCall != null) { + if (access.isRead) { + val getDescriptor = resultingDescriptor.getMethod + KotlinAccessorCallExpression(psi, this, resolvedCall, getDescriptor, null).accept(visitor) + } + + if (access.isWrite && setterValue != null) { + val setDescriptor = resultingDescriptor.setMethod + if (setDescriptor != null) { + KotlinAccessorCallExpression(psi, this, resolvedCall, setDescriptor, setterValue).accept(visitor) + } + } + } + } + } + + private tailrec fun findAssignment(prev: PsiElement?, element: PsiElement?): KtBinaryExpression? = when (element) { + is KtBinaryExpression -> if (element.left == prev && element.operationToken == KtTokens.EQ) element else null + is KtQualifiedExpression -> findAssignment(element, element.parent) + is KtSimpleNameExpression -> findAssignment(element, element.parent) + else -> null + } + + class KotlinAccessorCallExpression( + override val psi: KtElement, + override val uastParent: KotlinUSimpleReferenceExpression, + private val resolvedCall: ResolvedCall<*>, + private val accessorDescriptor: DeclarationDescriptor, + val setterValue: KtExpression? + ) : UCallExpression, JvmDeclarationUElement { + override val methodName: String? + get() = accessorDescriptor.name.asString() + + override val receiver: UExpression? + get() { + val containingElement = uastParent.uastParent + return if (containingElement is UQualifiedReferenceExpression && containingElement.selector == this) + containingElement.receiver + else + null + } + + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = psi + + override val annotations: List + get() = emptyList() + + override val receiverType by lz { + val type = (resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver)?.type ?: return@lz null + type.toPsiType(this, psi, boxed = true) + } + + override val methodIdentifier: UIdentifier? + get() = UIdentifier(uastParent.psi, this) + + override val classReference: UReferenceExpression? + get() = null + + override val valueArgumentCount: Int + get() = if (setterValue != null) 1 else 0 + + override val valueArguments by lz { + if (setterValue != null) + listOf(KotlinConverter.convertOrEmpty(setterValue, this)) + else + emptyList() + } + + override val typeArgumentCount: Int + get() = resolvedCall.typeArguments.size + + override val typeArguments by lz { + resolvedCall.typeArguments.values.map { it.toPsiType(this, psi, true) } + } + + override val returnType by lz { + (accessorDescriptor as? CallableDescriptor)?.returnType?.toPsiType(this, psi, boxed = false) + } + + override val kind: UastCallKind + get() = UastCallKind.METHOD_CALL + + override fun resolve(): PsiMethod? { + val source = accessorDescriptor.toSource() + return KotlinUFunctionCallExpression.resolveSource(accessorDescriptor, source) + } + } + + enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) { + READ(true, false), WRITE(false, true), READ_WRITE(true, true) + } + + private fun KtExpression.readWriteAccess(): ReferenceAccess { + var expression = getQualifiedExpressionForSelectorOrThis() + loop@ while (true) { + val parent = expression.parent + when (parent) { + is KtParenthesizedExpression, is KtAnnotatedExpression, is KtLabeledExpression -> expression = parent as KtExpression + else -> break@loop + } + } + + val assignment = expression.getAssignmentByLHS() + if (assignment != null) { + return when (assignment.operationToken) { + KtTokens.EQ -> ReferenceAccess.WRITE + else -> ReferenceAccess.READ_WRITE + } + } + + return if ((expression.parent as? KtUnaryExpression)?.operationToken + in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) }) + ReferenceAccess.READ_WRITE + else + ReferenceAccess.READ + } +} + +class KotlinClassViaConstructorUSimpleReferenceExpression( + override val psi: KtCallElement, + override val identifier: String, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression, KotlinUElementWithType { + override val resolvedName: String? + get() = (psi.getResolvedCall(psi.analyze())?.resultingDescriptor as? ConstructorDescriptor) + ?.containingDeclaration?.name?.asString() + + override fun resolve(): PsiElement? { + val resolvedCall = psi.getResolvedCall(psi.analyze()) + val resultingDescriptor = resolvedCall?.resultingDescriptor as? ConstructorDescriptor ?: return null + val clazz = resultingDescriptor.containingDeclaration + return clazz.toSource()?.getMaybeLightElement(this) + } +} + +class KotlinStringUSimpleReferenceExpression( + override val identifier: String, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression { + override val psi: PsiElement? + get() = null + override fun resolve() = null + override val resolvedName: String? + get() = identifier +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt index b87476a1f9b..9ae022699d3 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.USuperExpression +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUSuperExpression( override val psi: KtSuperExpression, @@ -30,7 +31,7 @@ class KotlinUSuperExpression( get() = psi.getLabelName() override val labelIdentifier: UIdentifier? - get() = psi.getTargetLabel()?.let { UIdentifier(it, this) } + get() = psi.getTargetLabel()?.let { KotlinUIdentifier(it, this) } override fun resolve() = psi.analyze()[BindingContext.LABEL_TARGET, psi.getTargetLabel()] } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt.173 new file mode 100644 index 00000000000..b87476a1f9b --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt.173 @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.psi.KtSuperExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.USuperExpression + +class KotlinUSuperExpression( + override val psi: KtSuperExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USuperExpression, KotlinUElementWithType, KotlinEvaluatableUElement { + override val label: String? + get() = psi.getLabelName() + + override val labelIdentifier: UIdentifier? + get() = psi.getTargetLabel()?.let { UIdentifier(it, this) } + + override fun resolve() = psi.analyze()[BindingContext.LABEL_TARGET, psi.getTargetLabel()] +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt index 4fecbaeb828..ebe356145f6 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtWhenEntry import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.kinds.KotlinSpecialExpressionKinds class KotlinUSwitchExpression( @@ -45,7 +46,7 @@ class KotlinUSwitchExpression( } override val switchIdentifier: UIdentifier - get() = UIdentifier(null, this) + get() = KotlinUIdentifier(null, this) } class KotlinUSwitchEntry( diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.173 new file mode 100644 index 00000000000..4fecbaeb828 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSwitchExpression.kt.173 @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtWhenEntry +import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.kinds.KotlinSpecialExpressionKinds + +class KotlinUSwitchExpression( + override val psi: KtWhenExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USwitchExpression, KotlinUElementWithType { + override val expression by lz { KotlinConverter.convertOrNull(psi.subjectExpression, this) } + + override val body: UExpressionList by lz { + object : KotlinUExpressionList(psi, KotlinSpecialExpressionKinds.WHEN, this@KotlinUSwitchExpression) { + override fun asRenderString() = expressions.joinToString("\n") { it.asRenderString().withMargin } + }.apply { + expressions = this@KotlinUSwitchExpression.psi.entries.map { KotlinUSwitchEntry(it, this) } + } + } + + override fun asRenderString() = buildString { + val expr = expression?.let { "(" + it.asRenderString() + ") " } ?: "" + appendln("switch $expr {") + appendln(body.asRenderString()) + appendln("}") + } + + override val switchIdentifier: UIdentifier + get() = UIdentifier(null, this) +} + +class KotlinUSwitchEntry( + override val psi: KtWhenEntry, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), USwitchClauseExpressionWithBody { + override val caseValues by lz { + psi.conditions.map { KotlinConverter.convertWhenCondition(it, this) ?: UastEmptyExpression } + } + + override val body: UExpressionList by lz { + object : KotlinUExpressionList(psi, KotlinSpecialExpressionKinds.WHEN_ENTRY, this@KotlinUSwitchEntry) { + override fun asRenderString() = buildString { + appendln("{") + expressions.forEach { appendln(it.asRenderString().withMargin) } + appendln("}") + } + }.apply { + val exprPsi = this@KotlinUSwitchEntry.psi.expression + val userExpressions = when (exprPsi) { + is KtBlockExpression -> exprPsi.statements.map { KotlinConverter.convertOrEmpty(it, this) } + else -> listOf(KotlinConverter.convertOrEmpty(exprPsi, this)) + } + expressions = userExpressions + object : UBreakExpression, JvmDeclarationUElement { + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + override val psi: PsiElement? + get() = null + override val label: String? + get() = null + override val uastParent: UElement? + get() = this@KotlinUSwitchEntry + override val annotations: List + get() = emptyList() + } + } + } + + override fun convertParent(): UElement? { + val result = KotlinConverter.unwrapElements(psi.parent)?.let { parentUnwrapped -> + KotlinUastLanguagePlugin().convertElementWithParent(parentUnwrapped, null) + } + return (result as? KotlinUSwitchExpression)?.body ?: result + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt index ca81276a4db..ce8ef9146a3 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.UThisExpression +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUThisExpression( override val psi: KtThisExpression, @@ -30,7 +31,7 @@ class KotlinUThisExpression( get() = psi.getLabelName() override val labelIdentifier: UIdentifier? - get() = psi.getTargetLabel()?.let { UIdentifier(it, this) } + get() = psi.getTargetLabel()?.let { KotlinUIdentifier(it, this) } override fun resolve() = psi.analyze()[BindingContext.LABEL_TARGET, psi.getTargetLabel()] } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.173 new file mode 100644 index 00000000000..ca81276a4db --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.173 @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.psi.KtThisExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UThisExpression + +class KotlinUThisExpression( + override val psi: KtThisExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UThisExpression, KotlinUElementWithType, KotlinEvaluatableUElement { + override val label: String? + get() = psi.getLabelName() + + override val labelIdentifier: UIdentifier? + get() = psi.getTargetLabel()?.let { UIdentifier(it, this) } + + override fun resolve() = psi.analyze()[BindingContext.LABEL_TARGET, psi.getTargetLabel()] +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt index 544ea9db537..db9c6cedd83 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt @@ -21,6 +21,7 @@ import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.UTryExpression import org.jetbrains.uast.UVariable +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUTryExpression( override val psi: KtTryExpression, @@ -37,7 +38,7 @@ class KotlinUTryExpression( get() = false override val tryIdentifier: UIdentifier - get() = UIdentifier(null, this) + get() = KotlinUIdentifier(null, this) override val finallyIdentifier: UIdentifier? get() = null diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.173 new file mode 100644 index 00000000000..544ea9db537 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUTryExpression.kt.173 @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.psi.KtTryExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UTryExpression +import org.jetbrains.uast.UVariable + +class KotlinUTryExpression( + override val psi: KtTryExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UTryExpression, KotlinUElementWithType { + override val tryClause by lz { KotlinConverter.convertOrEmpty(psi.tryBlock, this) } + override val catchClauses by lz { psi.catchClauses.map { KotlinUCatchClause(it, this) } } + override val finallyClause by lz { psi.finallyBlock?.finalExpression?.let { KotlinConverter.convertExpression(it, this) } } + + override val resourceVariables: List + get() = emptyList() + + override val hasResources: Boolean + get() = false + + override val tryIdentifier: UIdentifier + get() = UIdentifier(null, this) + + override val finallyIdentifier: UIdentifier? + get() = null +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt index 2032dfa4210..dd4dbf194e1 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.psi.KtWhileExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UIdentifier import org.jetbrains.uast.UWhileExpression +import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier class KotlinUWhileExpression( override val psi: KtWhileExpression, @@ -29,5 +30,5 @@ class KotlinUWhileExpression( override val body by lz { KotlinConverter.convertOrEmpty(psi.body, this) } override val whileIdentifier: UIdentifier - get() = UIdentifier(null, this) + get() = KotlinUIdentifier(null, this) } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt.173 new file mode 100644 index 00000000000..2032dfa4210 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUWhileExpression.kt.173 @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import org.jetbrains.kotlin.psi.KtWhileExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UWhileExpression + +class KotlinUWhileExpression( + override val psi: KtWhileExpression, + givenParent: UElement? +) : KotlinAbstractUExpression(givenParent), UWhileExpression { + override val condition by lz { KotlinConverter.convertOrEmpty(psi.condition, this) } + override val body by lz { KotlinConverter.convertOrEmpty(psi.body, this) } + + override val whileIdentifier: UIdentifier + get() = UIdentifier(null, this) +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt index 93aa080ecdc..9e43f412f48 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt @@ -70,7 +70,7 @@ internal fun DeclarationDescriptor.toSource(): PsiElement? { } } -internal fun lz(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer) +internal fun lz(initializer: () -> T) = lazy(LazyThreadSafetyMode.SYNCHRONIZED, initializer) internal fun KotlinType.toPsiType(source: UElement, element: KtElement, boxed: Boolean): PsiType { if (this.isError) return UastErrorType diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt.173 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt.173 new file mode 100644 index 00000000000..93aa080ecdc --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt.173 @@ -0,0 +1,213 @@ +/* + * Copyright 2010-2016 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.uast.kotlin + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiPrimitiveType +import com.intellij.psi.PsiType +import com.intellij.psi.impl.cache.TypeInfo +import com.intellij.psi.impl.compiled.ClsTypeElementImpl +import com.intellij.psi.impl.compiled.SignatureParsing +import com.intellij.psi.impl.compiled.StubBuildingVisitor +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiTypesUtil +import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype +import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.types.typeUtil.isInterface +import org.jetbrains.uast.* +import java.lang.ref.WeakReference +import java.text.StringCharacterIterator + +internal val KOTLIN_CACHED_UELEMENT_KEY = Key.create>("cached-kotlin-uelement") + +@Suppress("NOTHING_TO_INLINE") +internal inline fun String?.orAnonymous(kind: String = ""): String = this ?: "" + +internal fun DeclarationDescriptor.toSource(): PsiElement? { + return try { + DescriptorToSourceUtils.getEffectiveReferencedDescriptors(this) + .asSequence() + .mapNotNull { DescriptorToSourceUtils.getSourceFromDescriptor(it) } + .firstOrNull() + } + catch (e: Exception) { + Logger.getInstance("DeclarationDescriptor.toSource").error(e) + null + } +} + +internal fun lz(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer) + +internal fun KotlinType.toPsiType(source: UElement, element: KtElement, boxed: Boolean): PsiType { + if (this.isError) return UastErrorType + + (constructor.declarationDescriptor as? TypeAliasDescriptor)?.let { typeAlias -> + return typeAlias.expandedType.toPsiType(source, element, boxed) + } + + if (arguments.isEmpty()) { + val typeFqName = this.constructor.declarationDescriptor?.fqNameSafe?.asString() + fun PsiPrimitiveType.orBoxed() = if (boxed) getBoxedType(element) else this + val psiType = when (typeFqName) { + "kotlin.Int" -> PsiType.INT.orBoxed() + "kotlin.Long" -> PsiType.LONG.orBoxed() + "kotlin.Short" -> PsiType.SHORT.orBoxed() + "kotlin.Boolean" -> PsiType.BOOLEAN.orBoxed() + "kotlin.Byte" -> PsiType.BYTE.orBoxed() + "kotlin.Char" -> PsiType.CHAR.orBoxed() + "kotlin.Double" -> PsiType.DOUBLE.orBoxed() + "kotlin.Float" -> PsiType.FLOAT.orBoxed() + "kotlin.String" -> PsiType.getJavaLangString(element.manager, GlobalSearchScope.projectScope(element.project)) + else -> { + val typeConstructor = this.constructor + if (typeConstructor is IntegerValueTypeConstructor) { + typeConstructor.supertypes.first().toPsiType(source, element, boxed) + } else { + null + } + } + } + if (psiType != null) return psiType + } + + if (this.containsLocalTypes()) return UastErrorType + + val project = element.project + val typeMapper = ServiceManager.getService(project, KotlinUastBindingContextProviderService::class.java) + .getTypeMapper(element) ?: return UastErrorType + + val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.TYPE) + val typeMappingMode = if (boxed) TypeMappingMode.GENERIC_ARGUMENT else TypeMappingMode.DEFAULT + typeMapper.mapType(this, signatureWriter, typeMappingMode) + + val signature = StringCharacterIterator(signatureWriter.toString()) + + val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER) + val typeInfo = TypeInfo.fromString(javaType, false) + val typeText = TypeInfo.createTypeText(typeInfo) ?: return UastErrorType + + return ClsTypeElementImpl(source.getParentOfType(false)?.psi ?: element, typeText, '\u0000').type +} + +private fun KotlinType.containsLocalTypes(): Boolean { + val typeDeclarationDescriptor = this.constructor.declarationDescriptor + if (typeDeclarationDescriptor is ClassDescriptor && DescriptorUtils.isLocal(typeDeclarationDescriptor)) { + return true + } + + return arguments.any { !it.isStarProjection && it.type.containsLocalTypes() } +} + +internal fun KtTypeReference?.toPsiType(source: UElement, boxed: Boolean = false): PsiType { + if (this == null) return UastErrorType + return (analyze()[BindingContext.TYPE, this] ?: return UastErrorType).toPsiType(source, this, boxed) +} + +internal fun KtClassOrObject.toPsiType(): PsiType { + val lightClass = toLightClass() ?: return UastErrorType + return PsiTypesUtil.getClassType(lightClass) +} + +internal fun PsiElement.getMaybeLightElement(context: UElement): PsiElement? { + return when (this) { + is KtVariableDeclaration -> { + val lightElement = toLightElements().firstOrNull() + if (lightElement != null) return lightElement + + val languagePlugin = context.getLanguagePlugin() + val uElement = languagePlugin.convertElementWithParent(this, null) + when (uElement) { + is UDeclaration -> uElement.psi + is UDeclarationsExpression -> uElement.declarations.firstOrNull()?.psi + else -> null + } + } + is KtDeclaration -> toLightElements().firstOrNull() + is KtElement -> null + else -> this + } +} + +internal fun KtElement.resolveCallToDeclaration( + context: KotlinAbstractUElement, + resultingDescriptor: DeclarationDescriptor? = null +): PsiElement? { + val descriptor = resultingDescriptor ?: run { + val resolvedCall = getResolvedCall(analyze()) ?: return null + resolvedCall.resultingDescriptor + } + + return descriptor.toSource()?.getMaybeLightElement(context) +} + +internal fun KtExpression.unwrapBlockOrParenthesis(): KtExpression { + val innerExpression = KtPsiUtil.safeDeparenthesize(this) + if (innerExpression is KtBlockExpression) { + val statement = innerExpression.statements.singleOrNull() ?: return this + return KtPsiUtil.safeDeparenthesize(statement) + } + return innerExpression +} + +internal fun KtElement.analyze(): BindingContext { + if(containingFile !is KtFile) return BindingContext.EMPTY // EA-114080, EA-113475 + return ServiceManager.getService(project, KotlinUastBindingContextProviderService::class.java) + ?.getBindingContext(this) ?: BindingContext.EMPTY +} + +internal inline fun unwrap(element: P): P { + val unwrapped = if (element is T) element.psi else element + assert(unwrapped !is UElement) + return unwrapped as P +} + +internal fun KtExpression.getExpectedType(): KotlinType? = analyze()[BindingContext.EXPECTED_EXPRESSION_TYPE, this] + +internal fun KtTypeReference.getType(): KotlinType? = analyze()[BindingContext.TYPE, this] + +internal fun KotlinType.getFunctionalInterfaceType(source: UElement, element: KtElement): PsiType? = + takeIf { it.isInterface() && !it.isBuiltinFunctionalTypeOrSubtype }?.toPsiType(source, element, false) + +internal fun KotlinULambdaExpression.getFunctionalInterfaceType(): PsiType? { + val parent = psi.parent + return when(parent) { + is KtBinaryExpressionWithTypeRHS -> parent.right?.getType()?.getFunctionalInterfaceType(this, psi) + else -> psi.getExpectedType()?.getFunctionalInterfaceType(this, psi) + } +} + +internal fun unwrapFakeFileForLightClass(file: PsiFile): PsiFile = (file as? FakeFileForLightClass)?.ktFile ?: file diff --git a/plugins/uast-kotlin/testData/Anonymous.identifiers.txt b/plugins/uast-kotlin/testData/Anonymous.identifiers.txt new file mode 100644 index 00000000000..40cc421ef48 --- /dev/null +++ b/plugins/uast-kotlin/testData/Anonymous.identifiers.txt @@ -0,0 +1,31 @@ +java -> USimpleNameReferenceExpression (identifier = java) +io -> USimpleNameReferenceExpression (identifier = io) +Closeable -> USimpleNameReferenceExpression (identifier = Closeable) +java -> USimpleNameReferenceExpression (identifier = java) +io -> USimpleNameReferenceExpression (identifier = io) +InputStream -> USimpleNameReferenceExpression (identifier = InputStream) +foo -> UAnnotationMethod (name = foo) + runnable -> ULocalVariable (name = runnable) + object -> UClass (name = null) + Runnable -> USimpleNameReferenceExpression (identifier = Runnable) + run -> UAnnotationMethod (name = run) + runnable -> USimpleNameReferenceExpression (identifier = runnable) + run -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + runnable2 -> ULocalVariable (name = runnable2) + Runnable -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + runnable2 -> USimpleNameReferenceExpression (identifier = runnable2) + run -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + closeableRunnable -> ULocalVariable (name = closeableRunnable) + object -> UClass (name = null) + Runnable -> USimpleNameReferenceExpression (identifier = Runnable) + Closeable -> USimpleNameReferenceExpression (identifier = Closeable) + close -> UAnnotationMethod (name = close) + run -> UAnnotationMethod (name = run) + runnableIs -> ULocalVariable (name = runnableIs) + object -> UClass (name = null) + InputStream -> UObjectLiteralExpression + Runnable -> USimpleNameReferenceExpression (identifier = Runnable) + read -> UAnnotationMethod (name = read) + Int -> USimpleNameReferenceExpression (identifier = Int) + run -> UAnnotationMethod (name = run) diff --git a/plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt b/plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt new file mode 100644 index 00000000000..b26c4c639f6 --- /dev/null +++ b/plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt @@ -0,0 +1,13 @@ +Test -> UAnnotation (fqName = null) +A -> UClass (name = A) +MyAnnotation -> UClass (name = MyAnnotation) +text -> [!] UnknownKotlinExpression (VALUE_PARAMETER) +String -> USimpleNameReferenceExpression (identifier = String) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +B -> UClass (name = B) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +InB -> UClass (name = InB) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +object -> UClass (name = Companion) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +Obj -> UClass (name = Obj) diff --git a/plugins/uast-kotlin/testData/Constructors.identifiers.txt b/plugins/uast-kotlin/testData/Constructors.identifiers.txt new file mode 100644 index 00000000000..560dd4fdad2 --- /dev/null +++ b/plugins/uast-kotlin/testData/Constructors.identifiers.txt @@ -0,0 +1,67 @@ +A -> UClass (name = A) +str -> UParameter (name = str) +String -> USimpleNameReferenceExpression (identifier = String) +constructor -> UAnnotationMethod (name = A) +i -> UParameter (name = i) +Int -> USimpleNameReferenceExpression (identifier = Int) +this -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1)) +i -> USimpleNameReferenceExpression (identifier = i) +toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +AWithInit -> UClass (name = AWithInit) +str -> UParameter (name = str) +String -> USimpleNameReferenceExpression (identifier = String) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +constructor -> UAnnotationMethod (name = AWithInit) +i -> UParameter (name = i) +Int -> USimpleNameReferenceExpression (identifier = Int) +this -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1)) +i -> USimpleNameReferenceExpression (identifier = i) +toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +AWith2Init -> UClass (name = AWith2Init) +str -> UParameter (name = str) +String -> USimpleNameReferenceExpression (identifier = String) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) +constructor -> UAnnotationMethod (name = AWith2Init) +i -> UParameter (name = i) +Int -> USimpleNameReferenceExpression (identifier = Int) +this -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1)) +i -> USimpleNameReferenceExpression (identifier = i) +toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +AOnlyInit -> UClass (name = AOnlyInit) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) +AWithSecondary -> UClass (name = AWithSecondary) +a -> UField (name = a) +String -> USimpleNameReferenceExpression (identifier = String) +constructor -> UAnnotationMethod (name = AWithSecondary) +i -> UParameter (name = i) +Int -> USimpleNameReferenceExpression (identifier = Int) + a -> USimpleNameReferenceExpression (identifier = a) + i -> USimpleNameReferenceExpression (identifier = i) + toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +constructor -> UAnnotationMethod (name = AWithSecondary) +s -> UParameter (name = s) +String -> USimpleNameReferenceExpression (identifier = String) + a -> USimpleNameReferenceExpression (identifier = a) + s -> USimpleNameReferenceExpression (identifier = s) +AWithSecondaryInit -> UClass (name = AWithSecondaryInit) +a -> UField (name = a) +String -> USimpleNameReferenceExpression (identifier = String) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +constructor -> UAnnotationMethod (name = AWithSecondaryInit) +i -> UParameter (name = i) +Int -> USimpleNameReferenceExpression (identifier = Int) + a -> USimpleNameReferenceExpression (identifier = a) + i -> USimpleNameReferenceExpression (identifier = i) + toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) +constructor -> UAnnotationMethod (name = AWithSecondaryInit) +s -> UParameter (name = s) +String -> USimpleNameReferenceExpression (identifier = String) + a -> USimpleNameReferenceExpression (identifier = a) + s -> USimpleNameReferenceExpression (identifier = s) + local -> ULocalVariable (name = local) + String -> USimpleNameReferenceExpression (identifier = String) + s -> USimpleNameReferenceExpression (identifier = s) + local -> USimpleNameReferenceExpression (identifier = local) + toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.identifiers.txt b/plugins/uast-kotlin/testData/LocalDeclarations.identifiers.txt new file mode 100644 index 00000000000..0e28e7b947b --- /dev/null +++ b/plugins/uast-kotlin/testData/LocalDeclarations.identifiers.txt @@ -0,0 +1,14 @@ +foo -> UAnnotationMethod (name = foo) +Boolean -> USimpleNameReferenceExpression (identifier = Boolean) + Local -> UClass (name = Local) + bar -> ULambdaExpression + Local -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + baz -> ULocalVariable (name = baz) + Local -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + Int -> USimpleNameReferenceExpression (identifier = Int) + someLocalFun -> ULambdaExpression + text -> [!] UnknownKotlinExpression (VALUE_PARAMETER) + String -> USimpleNameReferenceExpression (identifier = String) + LocalObject -> UClass (name = LocalObject) + bar -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + Local -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.kt b/plugins/uast-kotlin/testData/LocalDeclarations.kt index ff45265e296..26a267e7d8d 100644 --- a/plugins/uast-kotlin/testData/LocalDeclarations.kt +++ b/plugins/uast-kotlin/testData/LocalDeclarations.kt @@ -8,5 +8,7 @@ fun foo(): Boolean { fun Int.someLocalFun(text: String) = 42 + object LocalObject + return bar() == Local() } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.kt.173 b/plugins/uast-kotlin/testData/LocalDeclarations.kt.173 new file mode 100644 index 00000000000..ff45265e296 --- /dev/null +++ b/plugins/uast-kotlin/testData/LocalDeclarations.kt.173 @@ -0,0 +1,12 @@ +fun foo(): Boolean { + class Local + fun bar() = Local() + + val baz = fun() { + Local() + } + + fun Int.someLocalFun(text: String) = 42 + + return bar() == Local() +} \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.log.txt b/plugins/uast-kotlin/testData/LocalDeclarations.log.txt index 26480d28ca7..bd4c47ba808 100644 --- a/plugins/uast-kotlin/testData/LocalDeclarations.log.txt +++ b/plugins/uast-kotlin/testData/LocalDeclarations.log.txt @@ -24,6 +24,11 @@ UFile (package = ) UParameter (name = text) UAnnotation (fqName = org.jetbrains.annotations.NotNull) ULiteralExpression (value = 42) + UDeclarationsExpression + UClass (name = LocalObject) + UField (name = INSTANCE) + UAnnotation (fqName = null) + UAnnotationMethod (name = LocalDeclarationsKt$foo$LocalObject) UReturnExpression UBinaryExpression (operator = ==) UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.log.txt.173 b/plugins/uast-kotlin/testData/LocalDeclarations.log.txt.173 new file mode 100644 index 00000000000..26480d28ca7 --- /dev/null +++ b/plugins/uast-kotlin/testData/LocalDeclarations.log.txt.173 @@ -0,0 +1,34 @@ +UFile (package = ) + UClass (name = LocalDeclarationsKt) + UAnnotationMethod (name = foo) + UBlockExpression + UDeclarationsExpression + UClass (name = Local) + UAnnotationMethod (name = LocalDeclarationsKt$foo$Local) + UDeclarationsExpression + UVariable (name = bar) + ULambdaExpression + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + UIdentifier (Identifier (Local)) + USimpleNameReferenceExpression (identifier = ) + UDeclarationsExpression + ULocalVariable (name = baz) + ULambdaExpression + UBlockExpression + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + UIdentifier (Identifier (Local)) + USimpleNameReferenceExpression (identifier = ) + UDeclarationsExpression + UVariable (name = someLocalFun) + ULambdaExpression + UParameter (name = text) + UAnnotation (fqName = org.jetbrains.annotations.NotNull) + ULiteralExpression (value = 42) + UReturnExpression + UBinaryExpression (operator = ==) + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (bar)) + USimpleNameReferenceExpression (identifier = bar) + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) + UIdentifier (Identifier (Local)) + USimpleNameReferenceExpression (identifier = ) diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.render.txt b/plugins/uast-kotlin/testData/LocalDeclarations.render.txt index b287c0bb920..b4a41c7ddb2 100644 --- a/plugins/uast-kotlin/testData/LocalDeclarations.render.txt +++ b/plugins/uast-kotlin/testData/LocalDeclarations.render.txt @@ -12,6 +12,10 @@ public final class LocalDeclarationsKt { var someLocalFun: kotlin.jvm.functions.Function2 = fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) { 42 } + public static final class LocalObject { + @null public static final var INSTANCE: LocalDeclarationsKt.foo.LocalObject + private fun LocalDeclarationsKt$foo$LocalObject() = UastEmptyExpression + } return bar() == () } } diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.render.txt.173 b/plugins/uast-kotlin/testData/LocalDeclarations.render.txt.173 new file mode 100644 index 00000000000..b287c0bb920 --- /dev/null +++ b/plugins/uast-kotlin/testData/LocalDeclarations.render.txt.173 @@ -0,0 +1,17 @@ +public final class LocalDeclarationsKt { + public static final fun foo() : boolean { + public static final class Local { + public fun LocalDeclarationsKt$foo$Local() = UastEmptyExpression + } + var bar: = fun () { + () + } + var baz: kotlin.jvm.functions.Function0 = fun () { + () + } + var someLocalFun: kotlin.jvm.functions.Function2 = fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) { + 42 + } + return bar() == () + } +} diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.types.txt b/plugins/uast-kotlin/testData/LocalDeclarations.types.txt index 8310b9965b3..7c1a696b7c6 100644 --- a/plugins/uast-kotlin/testData/LocalDeclarations.types.txt +++ b/plugins/uast-kotlin/testData/LocalDeclarations.types.txt @@ -24,6 +24,11 @@ UFile (package = ) [public final class LocalDeclarationsKt {...] UParameter (name = text) [@org.jetbrains.annotations.NotNull var text: java.lang.String] UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull] ULiteralExpression (value = 42) [42] : PsiType:int + UDeclarationsExpression [public static final class LocalObject {...}] + UClass (name = LocalObject) [public static final class LocalObject {...}] + UField (name = INSTANCE) [@null public static final var INSTANCE: LocalDeclarationsKt.foo.LocalObject] + UAnnotation (fqName = null) [@null] + UAnnotationMethod (name = LocalDeclarationsKt$foo$LocalObject) [private fun LocalDeclarationsKt$foo$LocalObject() = UastEmptyExpression] UReturnExpression [return bar() == ()] : PsiType:Void UBinaryExpression (operator = ==) [bar() == ()] : PsiType:boolean UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) [bar()] : PsiType: diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.types.txt.173 b/plugins/uast-kotlin/testData/LocalDeclarations.types.txt.173 new file mode 100644 index 00000000000..8310b9965b3 --- /dev/null +++ b/plugins/uast-kotlin/testData/LocalDeclarations.types.txt.173 @@ -0,0 +1,34 @@ +UFile (package = ) [public final class LocalDeclarationsKt {...] + UClass (name = LocalDeclarationsKt) [public final class LocalDeclarationsKt {...}] + UAnnotationMethod (name = foo) [public static final fun foo() : boolean {...}] + UBlockExpression [{...}] : PsiType:Void + UDeclarationsExpression [public static final class Local {...}] + UClass (name = Local) [public static final class Local {...}] + UAnnotationMethod (name = LocalDeclarationsKt$foo$Local) [public fun LocalDeclarationsKt$foo$Local() = UastEmptyExpression] + UDeclarationsExpression [var bar: = fun () {...}] + UVariable (name = bar) [var bar: = fun () {...}] + ULambdaExpression [fun () {...}] + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] : PsiType: + UIdentifier (Identifier (Local)) [UIdentifier (Identifier (Local))] + USimpleNameReferenceExpression (identifier = ) [] : PsiType: + UDeclarationsExpression [var baz: kotlin.jvm.functions.Function0 = fun () {...}] + ULocalVariable (name = baz) [var baz: kotlin.jvm.functions.Function0 = fun () {...}] + ULambdaExpression [fun () {...}] + UBlockExpression [{...}] : PsiType: + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] : PsiType: + UIdentifier (Identifier (Local)) [UIdentifier (Identifier (Local))] + USimpleNameReferenceExpression (identifier = ) [] : PsiType: + UDeclarationsExpression [var someLocalFun: kotlin.jvm.functions.Function2 = fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) {...}] + UVariable (name = someLocalFun) [var someLocalFun: kotlin.jvm.functions.Function2 = fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) {...}] + ULambdaExpression [fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) {...}] + UParameter (name = text) [@org.jetbrains.annotations.NotNull var text: java.lang.String] + UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull] + ULiteralExpression (value = 42) [42] : PsiType:int + UReturnExpression [return bar() == ()] : PsiType:Void + UBinaryExpression (operator = ==) [bar() == ()] : PsiType:boolean + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) [bar()] : PsiType: + UIdentifier (Identifier (bar)) [UIdentifier (Identifier (bar))] + USimpleNameReferenceExpression (identifier = bar) [bar] : PsiType: + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] : PsiType: + UIdentifier (Identifier (Local)) [UIdentifier (Identifier (Local))] + USimpleNameReferenceExpression (identifier = ) [] : PsiType: diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.values.txt b/plugins/uast-kotlin/testData/LocalDeclarations.values.txt index 69f4beabfb2..abbcb97f975 100644 --- a/plugins/uast-kotlin/testData/LocalDeclarations.values.txt +++ b/plugins/uast-kotlin/testData/LocalDeclarations.values.txt @@ -24,6 +24,11 @@ UFile (package = ) [public final class LocalDeclarationsKt {...] UParameter (name = text) [@org.jetbrains.annotations.NotNull var text: java.lang.String] UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull] ULiteralExpression (value = 42) [42] = 42 + UDeclarationsExpression [public static final class LocalObject {...}] = Undetermined + UClass (name = LocalObject) [public static final class LocalObject {...}] + UField (name = INSTANCE) [@null public static final var INSTANCE: LocalDeclarationsKt.foo.LocalObject] + UAnnotation (fqName = null) [@null] + UAnnotationMethod (name = LocalDeclarationsKt$foo$LocalObject) [private fun LocalDeclarationsKt$foo$LocalObject() = UastEmptyExpression] UReturnExpression [return bar() == ()] = Nothing UBinaryExpression (operator = ==) [bar() == ()] = Undetermined UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) [bar()] = external bar()() diff --git a/plugins/uast-kotlin/testData/LocalDeclarations.values.txt.173 b/plugins/uast-kotlin/testData/LocalDeclarations.values.txt.173 new file mode 100644 index 00000000000..69f4beabfb2 --- /dev/null +++ b/plugins/uast-kotlin/testData/LocalDeclarations.values.txt.173 @@ -0,0 +1,34 @@ +UFile (package = ) [public final class LocalDeclarationsKt {...] + UClass (name = LocalDeclarationsKt) [public final class LocalDeclarationsKt {...}] + UAnnotationMethod (name = foo) [public static final fun foo() : boolean {...}] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [public static final class Local {...}] = Undetermined + UClass (name = Local) [public static final class Local {...}] + UAnnotationMethod (name = LocalDeclarationsKt$foo$Local) [public fun LocalDeclarationsKt$foo$Local() = UastEmptyExpression] + UDeclarationsExpression [var bar: = fun () {...}] = Undetermined + UVariable (name = bar) [var bar: = fun () {...}] + ULambdaExpression [fun () {...}] = Undetermined + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] = external ()() + UIdentifier (Identifier (Local)) [UIdentifier (Identifier (Local))] + USimpleNameReferenceExpression (identifier = ) [] = external ()() + UDeclarationsExpression [var baz: kotlin.jvm.functions.Function0 = fun () {...}] = Undetermined + ULocalVariable (name = baz) [var baz: kotlin.jvm.functions.Function0 = fun () {...}] + ULambdaExpression [fun () {...}] = Undetermined + UBlockExpression [{...}] = external ()() + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] = external ()() + UIdentifier (Identifier (Local)) [UIdentifier (Identifier (Local))] + USimpleNameReferenceExpression (identifier = ) [] = external ()() + UDeclarationsExpression [var someLocalFun: kotlin.jvm.functions.Function2 = fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) {...}] = Undetermined + UVariable (name = someLocalFun) [var someLocalFun: kotlin.jvm.functions.Function2 = fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) {...}] + ULambdaExpression [fun (@org.jetbrains.annotations.NotNull var text: java.lang.String) {...}] = Undetermined + UParameter (name = text) [@org.jetbrains.annotations.NotNull var text: java.lang.String] + UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull] + ULiteralExpression (value = 42) [42] = 42 + UReturnExpression [return bar() == ()] = Nothing + UBinaryExpression (operator = ==) [bar() == ()] = Undetermined + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) [bar()] = external bar()() + UIdentifier (Identifier (bar)) [UIdentifier (Identifier (bar))] + USimpleNameReferenceExpression (identifier = bar) [bar] = external bar()() + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [()] = external ()() + UIdentifier (Identifier (Local)) [UIdentifier (Identifier (Local))] + USimpleNameReferenceExpression (identifier = ) [] = external ()() diff --git a/plugins/uast-kotlin/testData/SimpleAnnotated.identifiers.txt b/plugins/uast-kotlin/testData/SimpleAnnotated.identifiers.txt new file mode 100644 index 00000000000..9e2cb188e0c --- /dev/null +++ b/plugins/uast-kotlin/testData/SimpleAnnotated.identifiers.txt @@ -0,0 +1,8 @@ +SimpleAnnotated -> UClass (name = SimpleAnnotated) +Suppress -> UAnnotation (fqName = kotlin.Suppress) +method -> UAnnotationMethod (name = method) + println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) +kotlin -> USimpleNameReferenceExpression (identifier = kotlin) +SinceKotlin -> UAnnotation (fqName = kotlin.SinceKotlin) +property -> UField (name = property) +String -> USimpleNameReferenceExpression (identifier = String) diff --git a/plugins/uast-kotlin/testData/SimpleAnnotated.kt b/plugins/uast-kotlin/testData/SimpleAnnotated.kt index b0742abd2f4..eba544fa27c 100644 --- a/plugins/uast-kotlin/testData/SimpleAnnotated.kt +++ b/plugins/uast-kotlin/testData/SimpleAnnotated.kt @@ -4,6 +4,6 @@ class SimpleAnnotated { println("Hello, world!") } - @SinceKotlin("1.0") + @kotlin.SinceKotlin("1.0") val property: String = "Mary" } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/SimpleAnnotated.kt.173 b/plugins/uast-kotlin/testData/SimpleAnnotated.kt.173 new file mode 100644 index 00000000000..b0742abd2f4 --- /dev/null +++ b/plugins/uast-kotlin/testData/SimpleAnnotated.kt.173 @@ -0,0 +1,9 @@ +class SimpleAnnotated { + @Suppress("abc") + fun method() { + println("Hello, world!") + } + + @SinceKotlin("1.0") + val property: String = "Mary" +} \ No newline at end of file diff --git a/plugins/uast-kotlin/tests/AbstractKotlinIdentifiersTest.kt b/plugins/uast-kotlin/tests/AbstractKotlinIdentifiersTest.kt new file mode 100644 index 00000000000..3809ff99f28 --- /dev/null +++ b/plugins/uast-kotlin/tests/AbstractKotlinIdentifiersTest.kt @@ -0,0 +1,15 @@ +package org.jetbrains.uast.test.kotlin + +import org.jetbrains.uast.test.common.IdentifiersTestBase +import java.io.File + + +abstract class AbstractKotlinIdentifiersTest : AbstractKotlinUastTest(), IdentifiersTestBase { + + private fun getTestFile(testName: String, ext: String) = + File(File(AbstractKotlinUastTest.TEST_KOTLIN_MODEL_DIR, testName).canonicalPath + '.' + ext) + + override fun getIdentifiersFile(testName: String): File = getTestFile(testName, "identifiers.txt") + + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt b/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt index f0fd8111308..10c2ce5acb9 100644 --- a/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt +++ b/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt @@ -8,7 +8,7 @@ import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.utils.addToStdlib.assertedCast import org.jetbrains.uast.JvmDeclarationUElement -import org.jetbrains.uast.UDeclaration +import org.jetbrains.uast.UAnchorOwner import org.jetbrains.uast.UElement import org.jetbrains.uast.UFile import org.jetbrains.uast.kotlin.KOTLIN_CACHED_UELEMENT_KEY @@ -90,7 +90,7 @@ abstract class AbstractKotlinRenderLogTest : AbstractKotlinUastTest(), RenderLog node.containingFile.assertedCast { "containingFile should be KtFile for ${node.asLogString()}" } } - val anchorPsi = (node as? UDeclaration)?.uastAnchor?.psi + val anchorPsi = (node as? UAnchorOwner)?.uastAnchor?.sourcePsi if (anchorPsi != null) { anchorPsi.containingFile.assertedCast { "uastAnchor.containingFile should be KtFile for ${node.asLogString()}" } } @@ -104,14 +104,16 @@ abstract class AbstractKotlinRenderLogTest : AbstractKotlinUastTest(), RenderLog accept(object : UastVisitor { override fun visitElement(node: UElement): Boolean { + if (node is UAnchorOwner) { + node.uastAnchor?.let { visitElement(it) } + } + val jvmDeclaration = node as? JvmDeclarationUElement ?: throw AssertionError("${node.javaClass} should implement 'JvmDeclarationUElement'") jvmDeclaration.sourcePsi?.let { assertTrue("sourcePsi should be physical but ${it.javaClass} found for [${it.text}] " + - "for ${jvmDeclaration.javaClass}->${jvmDeclaration.uastParent?.javaClass}", - it is KtElement || it is LeafPsiElement - ) + "for ${jvmDeclaration.javaClass}->${jvmDeclaration.uastParent?.javaClass}",it is LeafPsiElement || it is KtElement|| it is LeafPsiElement) } jvmDeclaration.javaPsi?.let { assertTrue("javaPsi should be light but ${it.javaClass} found for [${it.text}] " + diff --git a/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt.173 b/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt.173 new file mode 100644 index 00000000000..f0fd8111308 --- /dev/null +++ b/plugins/uast-kotlin/tests/AbstractKotlinRenderLogTest.kt.173 @@ -0,0 +1,134 @@ +package org.jetbrains.uast.test.kotlin + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiRecursiveElementVisitor +import com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.utils.addToStdlib.assertedCast +import org.jetbrains.uast.JvmDeclarationUElement +import org.jetbrains.uast.UDeclaration +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UFile +import org.jetbrains.uast.kotlin.KOTLIN_CACHED_UELEMENT_KEY +import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin +import org.jetbrains.uast.test.common.RenderLogTestBase +import org.jetbrains.uast.visitor.UastVisitor +import org.junit.Assert +import java.io.File +import java.util.* + +abstract class AbstractKotlinRenderLogTest : AbstractKotlinUastTest(), RenderLogTestBase { + override fun getTestFile(testName: String, ext: String) = + File(File(TEST_KOTLIN_MODEL_DIR, testName).canonicalPath + '.' + ext) + + override fun check(testName: String, file: UFile) { + check(testName, file, true) + } + + fun check(testName: String, file: UFile, checkParentConsistency: Boolean) { + super.check(testName, file) + + if (checkParentConsistency) { + checkParentConsistency(file) + } + + file.checkContainingFileForAllElements() + file.checkJvmDeclarationsImplementations() + } + + private fun checkParentConsistency(file: UFile) { + val parentMap = mutableMapOf() + + file.accept(object : UastVisitor { + private val parentStack = Stack() + + override fun visitElement(node: UElement): Boolean { + val parent = node.uastParent + if (parent == null) { + Assert.assertTrue("Wrong parent of $node", parentStack.empty()) + } + else { + Assert.assertEquals("Wrong parent of $node", parentStack.peek(), parent) + } + node.psi?.let { + if (it !in parentMap) { + parentMap[it] = parentStack.reversed().joinToString { it.asLogString() } + } + } + parentStack.push(node) + return false + } + + override fun afterVisitElement(node: UElement) { + super.afterVisitElement(node) + parentStack.pop() + } + }) + + file.psi.clearUastCaches() + + file.psi.accept(object : PsiRecursiveElementVisitor() { + override fun visitElement(element: PsiElement) { + val uElement = KotlinUastLanguagePlugin().convertElementWithParent(element, null) + val expectedParents = parentMap[element] + if (expectedParents != null) { + assertNotNull("Expected to be able to convert PSI element $element", uElement) + val parents = generateSequence(uElement!!.uastParent) { it.uastParent }.joinToString { it.asLogString() } + assertEquals("Inconsistent parents for ${uElement.asRenderString()}(${uElement.asLogString()})(${uElement.javaClass}) (converted from $element[${element.text}])", expectedParents, parents) + } + super.visitElement(element) + } + }) + } + + private fun UFile.checkContainingFileForAllElements() { + accept(object : UastVisitor { + override fun visitElement(node: UElement): Boolean { + if (node is PsiElement) { + node.containingFile.assertedCast { "containingFile should be KtFile for ${node.asLogString()}" } + } + + val anchorPsi = (node as? UDeclaration)?.uastAnchor?.psi + if (anchorPsi != null) { + anchorPsi.containingFile.assertedCast { "uastAnchor.containingFile should be KtFile for ${node.asLogString()}" } + } + + return false + } + }) + } + + private fun UFile.checkJvmDeclarationsImplementations() { + accept(object : UastVisitor { + override fun visitElement(node: UElement): Boolean { + + val jvmDeclaration = node as? JvmDeclarationUElement + ?: throw AssertionError("${node.javaClass} should implement 'JvmDeclarationUElement'") + + jvmDeclaration.sourcePsi?.let { + assertTrue("sourcePsi should be physical but ${it.javaClass} found for [${it.text}] " + + "for ${jvmDeclaration.javaClass}->${jvmDeclaration.uastParent?.javaClass}", + it is KtElement || it is LeafPsiElement + ) + } + jvmDeclaration.javaPsi?.let { + assertTrue("javaPsi should be light but ${it.javaClass} found for [${it.text}] " + + "for ${jvmDeclaration.javaClass}->${jvmDeclaration.uastParent?.javaClass}", it !is KtElement) + } + + return false + } + }) + } +} + +private fun PsiFile.clearUastCaches() { + accept(object : PsiRecursiveElementVisitor() { + override fun visitElement(element: PsiElement) { + super.visitElement(element) + element.putUserData(KOTLIN_CACHED_UELEMENT_KEY, null) + } + }) +} diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt index 4e673d38b1a..4a508dde279 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt @@ -6,7 +6,6 @@ import com.intellij.testFramework.UsefulTestCase import org.jetbrains.kotlin.asJava.toLightAnnotation import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType -import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.uast.* @@ -219,9 +218,10 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { @Test fun testSimpleAnnotated() { doTest("SimpleAnnotated") { _, file -> - file.findElementByTextFromPsi("@SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field -> + file.findElementByTextFromPsi("@kotlin.SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field -> val annotation = field.annotations.assertedFind("kotlin.SinceKotlin") { it.qualifiedName } - Assert.assertEquals(annotation.findDeclaredAttributeValue("version")?.evaluateString(), "1.0") + Assert.assertEquals("1.0", annotation.findDeclaredAttributeValue("version")?.evaluateString()) + Assert.assertEquals("SinceKotlin", annotation.cast().uastAnchor?.sourcePsi?.text) } } } @@ -303,10 +303,44 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java) ?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation") assertEquals("Annotation", lightAnnotation.qualifiedName) + assertEquals("Annotation", (convertedUAnnotation as UAnchorOwner).uastAnchor?.sourcePsi?.text) } } + @Test + fun testParametersDisorder() = doTest("ParametersDisorder") { _, file -> + + fun assertArguments(argumentsInPositionalOrder: List?, refText: String) = + file.findElementByTextFromPsi(refText).let { call -> + if (call !is UCallExpressionEx) throw AssertionError("${call.javaClass} is not a UCallExpressionEx") + Assert.assertEquals( + argumentsInPositionalOrder, + call.resolve()?.let { psiMethod -> + (0 until psiMethod.parameterList.parametersCount).map { + call.getArgumentForParameter(it)?.asRenderString() + } + } + ) + } + + + assertArguments(listOf("2", "2.2"), "global(b = 2.2F, a = 2)") + assertArguments(listOf(null, "\"bbb\""), "withDefault(d = \"bbb\")") + assertArguments(listOf("1.3", "3.4"), "atan2(1.3, 3.4)") + assertArguments(null, "unresolvedMethod(\"param1\", \"param2\")") + assertArguments(listOf("\"%i %i %i\"", "varargs 1 : 2 : 3"), "format(\"%i %i %i\", 1, 2, 3)") + assertArguments(listOf("\"%i %i %i\"", "varargs arrayOf(1, 2, 3)"), "format(\"%i %i %i\", arrayOf(1, 2, 3))") + assertArguments( + listOf("\"%i %i %i\"", "varargs arrayOf(1, 2, 3) : arrayOf(4, 5, 6)"), + "format(\"%i %i %i\", arrayOf(1, 2, 3), arrayOf(4, 5, 6))" + ) + assertArguments(listOf("\"%i %i %i\"", "\"\".chunked(2).toTypedArray()"), "format(\"%i %i %i\", *\"\".chunked(2).toTypedArray())") + assertArguments(listOf("\"def\"", "8", "7.0"), "with2Receivers(8, 7.0F)") + assertArguments(listOf("\"foo\"", "1"), "object : Parent(b = 1, a = \"foo\")\n") + } + } -fun Iterable.assertedFind(value: R, transform: (T) -> R): T = find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}") +fun Iterable.assertedFind(value: R, transform: (T) -> R): T = + find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}") diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 new file mode 100644 index 00000000000..4e673d38b1a --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.173 @@ -0,0 +1,312 @@ +package org.jetbrains.uast.test.kotlin + +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiModifier +import com.intellij.testFramework.UsefulTestCase +import org.jetbrains.kotlin.asJava.toLightAnnotation +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList +import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase +import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.uast.* +import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin +import org.jetbrains.uast.test.env.findElementByText +import org.jetbrains.uast.test.env.findElementByTextFromPsi +import org.jetbrains.uast.visitor.AbstractUastVisitor +import org.junit.Assert +import org.junit.Test + + +class KotlinUastApiTest : AbstractKotlinUastTest() { + override fun check(testName: String, file: UFile) { + } + + @Test fun testAnnotationParameters() { + doTest("AnnotationParameters") { _, file -> + val annotation = file.findElementByText("@IntRange(from = 10, to = 0)") + assertEquals(annotation.findAttributeValue("from")?.evaluate(), 10) + val toAttribute = annotation.findAttributeValue("to")!! + assertEquals(toAttribute.evaluate(), 0) + KtUsefulTestCase.assertInstanceOf(annotation.psi.toUElement(), UAnnotation::class.java) + KtUsefulTestCase.assertInstanceOf( + annotation.psi.cast().toLightAnnotation().toUElement(), + UAnnotation::class.java + ) + KtUsefulTestCase.assertInstanceOf(toAttribute.uastParent, UNamedExpression::class.java) + KtUsefulTestCase.assertInstanceOf(toAttribute.psi.toUElement()?.uastParent, UNamedExpression::class.java) + } + } + + @Test fun testConvertStringTemplate() { + doTest("StringTemplateInClass") { _, file -> + val literalExpression = file.findElementByText("lorem") + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + val literalExpressionAgain = psi.toUElement() + Assert.assertTrue(literalExpressionAgain is ULiteralExpression) + + } + } + + @Test fun testConvertStringTemplateWithExpectedType() { + doTest("StringTemplateWithVar") { _, file -> + val index = file.psi.text.indexOf("foo") + val stringTemplate = file.psi.findElementAt(index)!!.getParentOfType(false) + val uLiteral = stringTemplate.toUElementOfType() + assertNull(uLiteral) + } + } + + @Test fun testNameContainingFile() { + doTest("NameContainingFile") { _, file -> + val foo = file.findElementByText("class Foo") + assertEquals(file.psi, foo.nameIdentifier!!.containingFile) + + val bar = file.findElementByText("fun bar() {}") + assertEquals(file.psi, bar.nameIdentifier!!.containingFile) + + val xyzzy = file.findElementByText("val xyzzy: Int = 0") + assertEquals(file.psi, xyzzy.nameIdentifier!!.containingFile) + } + } + + @Test fun testInterfaceMethodWithBody() { + doTest("DefaultImpls") { _, file -> + val bar = file.findElementByText("fun bar() = \"Hello!\"") + assertFalse(bar.containingFile.text!!, bar.psi.modifierList.hasExplicitModifier(PsiModifier.DEFAULT)) + assertTrue(bar.containingFile.text!!, bar.psi.modifierList.hasModifierProperty(PsiModifier.DEFAULT)) + } + } + + @Test fun testSAM() { + doTest("SAM") { _, file -> + assertNull(file.findElementByText("{ /* Not SAM */ }").functionalInterfaceType) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Variable */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Assignment */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Type Cast */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Argument */}").functionalInterfaceType?.canonicalText) + + assertEquals("java.lang.Runnable", + file.findElementByText("{/* Return */}").functionalInterfaceType?.canonicalText) + } + } + + @Test fun testParameterPropertyWithAnnotation() { + doTest("ParameterPropertyWithAnnotation") { _, file -> + val test1 = file.classes.find { it.name == "Test1" }!! + + val constructor1 = test1.methods.find { it.name == "Test1" }!! + assertTrue(constructor1.uastParameters.first().annotations.any { it.qualifiedName == "MyAnnotation" }) + + val getter1 = test1.methods.find { it.name == "getBar" }!! + assertFalse(getter1.annotations.any { it.qualifiedName == "MyAnnotation" }) + + val setter1 = test1.methods.find { it.name == "setBar" }!! + assertFalse(setter1.annotations.any { it.qualifiedName == "MyAnnotation" }) + assertFalse(setter1.uastParameters.first().annotations.any { it.qualifiedName == "MyAnnotation" }) + + + val test2 = file.classes.find { it.name == "Test2" }!! + val constructor2 = test2.methods.find { it.name == "Test2" }!! + assertFalse(constructor2.uastParameters.first().annotations.any { it.qualifiedName?.startsWith("MyAnnotation") ?: false }) + + val getter2 = test2.methods.find { it.name == "getBar" }!! + getter2.annotations.single { it.qualifiedName == "MyAnnotation" } + + val setter2 = test2.methods.find { it.name == "setBar" }!! + setter2.annotations.single { it.qualifiedName == "MyAnnotation2" } + setter2.uastParameters.first().annotations.single { it.qualifiedName == "MyAnnotation3" } + + test2.fields.find { it.name == "bar" }!!.annotations.single { it.qualifiedName == "MyAnnotation5" } + } + } + + @Test fun testConvertTypeInAnnotation() { + doTest("TypeInAnnotation") { _, file -> + val index = file.psi.text.indexOf("Test") + val element = file.psi.findElementAt(index)!!.getParentOfType(false)!! + assertNotNull(element.getUastParentOfType(UAnnotation::class.java)) + } + } + + @Test fun testElvisType() { + doTest("ElvisType") { _, file -> + val elvisExpression = file.findElementByText("text ?: return") + assertEquals("String", elvisExpression.getExpressionType()!!.presentableText) + } + } + + @Test fun testFindAttributeDefaultValue() { + doTest("AnnotationParameters") { _, file -> + val witDefaultValue = file.findElementByText("@WithDefaultValue") + assertEquals(42, witDefaultValue.findAttributeValue("value")!!.evaluate()) + assertEquals(42, witDefaultValue.findAttributeValue(null)!!.evaluate()) + } + } + + @Test fun testIfCondition() { + doTest("IfStatement") { _, file -> + val psiFile = file.psi + val element = psiFile.findElementAt(psiFile.text.indexOf("\"abc\""))!! + val binaryExpression = element.getParentOfType(false)!! + val uBinaryExpression = KotlinUastLanguagePlugin().convertElementWithParent(binaryExpression, null)!! + UsefulTestCase.assertInstanceOf(uBinaryExpression.uastParent, UIfExpression::class.java) + } + } + + @Test + fun testWhenStringLiteral() { + doTest("WhenStringLiteral") { _, file -> + + file.findElementByTextFromPsi("abc").let { literalExpression -> + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java) + } + + file.findElementByTextFromPsi("def").let { literalExpression -> + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java) + } + + file.findElementByTextFromPsi("def1").let { literalExpression -> + val psi = literalExpression.psi!! + Assert.assertTrue(psi is KtLiteralStringTemplateEntry) + UsefulTestCase.assertInstanceOf(literalExpression.uastParent, UBlockExpression::class.java) + } + + + } + } + + @Test + fun testWhenAndDestructing() { + doTest("WhenAndDestructing") { _, file -> + + file.findElementByTextFromPsi("val (bindingContext, statementFilter) = arr").let { e -> + val uBlockExpression = e.getParentOfType() + Assert.assertNotNull(uBlockExpression) + val uMethod = uBlockExpression!!.getParentOfType() + Assert.assertNotNull(uMethod) + } + + } + } + + @Test + fun testBrokenMethodTypeResolve() { + doTest("BrokenMethod") { _, file -> + + file.accept(object : AbstractUastVisitor() { + override fun visitCallExpression(node: UCallExpression): Boolean { + node.returnType + return false + } + }) + } + } + + @Test + fun testSimpleAnnotated() { + doTest("SimpleAnnotated") { _, file -> + file.findElementByTextFromPsi("@SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field -> + val annotation = field.annotations.assertedFind("kotlin.SinceKotlin") { it.qualifiedName } + Assert.assertEquals(annotation.findDeclaredAttributeValue("version")?.evaluateString(), "1.0") + } + } + } + + + fun UFile.checkUastSuperTypes(refText: String, superTypes: List) { + findElementByTextFromPsi(refText, false).let { + assertEquals("base classes", superTypes, it.uastSuperTypes.map { it.getQualifiedName() }) + } + } + + + @Test + fun testSuperTypes() { + doTest("SuperCalls") { _, file -> + file.checkUastSuperTypes("B", listOf("A")) + file.checkUastSuperTypes("O", listOf("A")) + file.checkUastSuperTypes("innerObject ", listOf("A")) + file.checkUastSuperTypes("InnerClass", listOf("A")) + file.checkUastSuperTypes("object : A(\"textForAnon\")", listOf("A")) + } + } + + @Test + fun testAnonymousSuperTypes() { + doTest("Anonymous") { _, file -> + file.checkUastSuperTypes("object : Runnable { override fun run() {} }", listOf("java.lang.Runnable")) + file.checkUastSuperTypes( + "object : Runnable, Closeable { override fun close() {} override fun run() {} }", + listOf("java.lang.Runnable", "java.io.Closeable") + ) + file.checkUastSuperTypes( + "object : InputStream(), Runnable { override fun read(): Int = 0; override fun run() {} }", + listOf("java.io.InputStream", "java.lang.Runnable") + ) + } + } + + @Test + fun testLiteralArraysTypes() { + doTest("AnnotationParameters") { _, file -> + file.findElementByTextFromPsi("intArrayOf(1, 2, 3)").let { field -> + Assert.assertEquals("PsiType:int[]", field.returnType.toString()) + } + file.findElementByTextFromPsi("[1, 2, 3]").let { field -> + Assert.assertEquals("PsiType:int[]", field.returnType.toString()) + Assert.assertEquals("PsiType:int", field.typeArguments.single().toString()) + } + file.findElementByTextFromPsi("[\"a\", \"b\", \"c\"]").let { field -> + Assert.assertEquals("PsiType:String[]", field.returnType.toString()) + Assert.assertEquals("PsiType:String", field.typeArguments.single().toString()) + } + + } + } + + @Test + fun testTypeAliases() { + doTest("TypeAliases") { _, file -> + val g = (file.psi as KtFile).declarations.single { it.name == "G" } as KtTypeAlias + val originalType = g.getTypeReference()!!.typeElement as KtFunctionType + val originalTypeParameters = originalType.parameterList.toUElement() as UDeclarationsExpression + Assert.assertTrue((originalTypeParameters.declarations.single() as UParameter).type.isValid) + } + } + + @Test + fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> + file.findElementByTextFromPsi("@AnnotationArray(value = Annotation())") + .findElementByTextFromPsi("Annotation()") + .sourcePsiElement + .let { referenceExpression -> + val convertedUAnnotation = referenceExpression + .cast() + .toUElementOfType() + ?: throw AssertionError("haven't got annotation from $referenceExpression(${referenceExpression?.javaClass})") + + assertEquals("Annotation", convertedUAnnotation.qualifiedName) + val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java) + ?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation") + assertEquals("Annotation", lightAnnotation.qualifiedName) + } + } + + +} + +fun Iterable.assertedFind(value: R, transform: (T) -> R): T = find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}") diff --git a/plugins/uast-kotlin/tests/KotlinUastIdentifiersTest.kt b/plugins/uast-kotlin/tests/KotlinUastIdentifiersTest.kt new file mode 100644 index 00000000000..d45325d1da4 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastIdentifiersTest.kt @@ -0,0 +1,27 @@ +/* + * 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.uast.test.kotlin + +import org.junit.Test + +class KotlinUastIdentifiersTest : AbstractKotlinIdentifiersTest() { + + @Test + fun testClassAnnotation() = doTest("ClassAnnotation") + + @Test + fun testLocalDeclarations() = doTest("LocalDeclarations") + + @Test + fun testConstructors() = doTest("Constructors") + + @Test + fun testSimpleAnnotated() = doTest("SimpleAnnotated") + + @Test + fun testAnonymous() = doTest("Anonymous") + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/tests/org/jetbrains/uast/test/common/IdentifiersTestBase.kt b/plugins/uast-kotlin/tests/org/jetbrains/uast/test/common/IdentifiersTestBase.kt new file mode 100644 index 00000000000..0af939c673a --- /dev/null +++ b/plugins/uast-kotlin/tests/org/jetbrains/uast/test/common/IdentifiersTestBase.kt @@ -0,0 +1,82 @@ +/* + * 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.uast.test.common + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.uast.* +import org.jetbrains.uast.test.env.assertEqualsToFile +import org.jetbrains.uast.visitor.AbstractUastVisitor +import org.junit.ComparisonFailure +import java.io.File + +interface IdentifiersTestBase { + fun getIdentifiersFile(testName: String): File + + private fun UFile.asIdentifiersWithParents(): String { + val builder = StringBuilder() + var level = 0 + (this.psi as KtFile).accept(object : PsiElementVisitor() { + override fun visitElement(element: PsiElement) { + val uIdentifier = element.toUElementOfType() + if (uIdentifier != null) { + builder.append(" ".repeat(level)) + builder.append(uIdentifier.sourcePsiElement!!.text) + builder.append(" -> ") + builder.append(uIdentifier.uastParent?.asLogString()) + builder.appendln() + } + if (element is KtBlockExpression) level++ + element.acceptChildren(this) + if (element is KtBlockExpression) level-- + } + }) + return builder.toString() + } + + private fun UFile.testIdentifiersParents() { + accept(object : AbstractUastVisitor() { + override fun visitElement(node: UElement): Boolean { + if (node is UAnchorOwner) { + val uastAnchor = node.uastAnchor ?: return false + assertParents(node, uastAnchor) + val uastAnchorFromSource = (uastAnchor.sourcePsi ?: return false).toUElementOfType() + assertParents(node, uastAnchorFromSource) + } + return false + } + }) + } + + fun assertParents(node: UAnchorOwner, uastAnchor: UIdentifier?) { + //skipping such elements because they are ambiguous (properties and getters for instance) + if (node.sourcePsi.toUElement() != node) return + if (uastAnchor == null) + throw AssertionError("no uast anchor for ${node.uastAnchor?.sourcePsi}(${node.uastAnchor?.sourcePsi?.javaClass}) for node = $node") + val nodeParents = node.withContainingElements.log() + val anchorParentParents = uastAnchor.uastParent?.withContainingElements?.log() ?: "" + // dropping node itself because we allow children to share identifiers with parents (primary constructor for instance) + val parentsSuffix = node.withContainingElements.drop(1).log() + if (!anchorParentParents.endsWith(parentsSuffix)) + throw ComparisonFailure( + "wrong parents for '${uastAnchor.sourcePsi?.text}' owner: $node[${node.sourcePsi}[${node.sourcePsi?.text}]]", + nodeParents, + anchorParentParents + ) + } + + private fun Sequence.log() = this.joinToString { it.asLogString() } + + fun check(testName: String, file: UFile) { + val valuesFile = getIdentifiersFile(testName) + + assertEqualsToFile("Identifiers", valuesFile, file.asIdentifiersWithParents()) + file.testIdentifiersParents() + } + +} diff --git a/prepare/compiler/build.gradle.kts b/prepare/compiler/build.gradle.kts index c86dc575fa0..5855d87c110 100644 --- a/prepare/compiler/build.gradle.kts +++ b/prepare/compiler/build.gradle.kts @@ -73,7 +73,7 @@ dependencies { fatJarContents(intellijDep()) { includeIntellijCoreJarDependencies(project, { !(it.startsWith("jdom") || it.startsWith("log4j")) }) } fatJarContents(intellijDep()) { includeJars("jna-platform") } fatJarContentsStripServices(intellijDep("jps-standalone")) { includeJars("jps-model") } - fatJarContentsStripMetadata(intellijDep()) { includeJars("oromatcher", "jdom", "log4j") } + fatJarContentsStripMetadata(intellijDep()) { includeJars("oro-2.0.8", "jdom", "log4j") } } diff --git a/prepare/compiler/build.gradle.kts.173 b/prepare/compiler/build.gradle.kts.173 new file mode 100644 index 00000000000..c86dc575fa0 --- /dev/null +++ b/prepare/compiler/build.gradle.kts.173 @@ -0,0 +1,132 @@ +import java.io.File +import proguard.gradle.ProGuardTask +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.file.DuplicatesStrategy + +description = "Kotlin Compiler" + +plugins { + `java` +} + +// You can run Gradle with "-Pkotlin.build.proguard=true" to enable ProGuard run on kotlin-compiler.jar (on TeamCity, ProGuard always runs) +val shrink = + findProperty("kotlin.build.proguard")?.toString()?.toBoolean() + ?: hasProperty("teamcity") + +val compilerManifestClassPath = + "kotlin-stdlib.jar kotlin-reflect.jar kotlin-script-runtime.jar" + +val fatJarContents by configurations.creating + +val fatJarContentsStripMetadata by configurations.creating +val fatJarContentsStripServices by configurations.creating +val fatSourcesJarContents by configurations.creating +val proguardLibraryJars by configurations.creating +val fatJar by configurations.creating +val compilerJar by configurations.creating +val archives by configurations +val compile by configurations + +val compilerBaseName = name + +val outputJar = File(buildDir, "libs", "$compilerBaseName.jar") + +val compilerModules: Array by rootProject.extra + +compilerModules.forEach { evaluationDependsOn(it) } + +val compiledModulesSources = compilerModules.map { + project(it).the().sourceSets.getByName("main").allSource +} + +dependencies { + compilerModules.forEach { + fatJarContents(project(it)) { isTransitive = false } + } + compiledModulesSources.forEach { + fatSourcesJarContents(it) + } + + fatJarContents(project(":core:builtins", configuration = "builtins")) + fatJarContents(commonDep("javax.inject")) + fatJarContents(commonDep("org.jline", "jline")) + fatJarContents(commonDep("org.fusesource.jansi", "jansi")) + fatJarContents(protobufFull()) + fatJarContents(commonDep("com.google.code.findbugs", "jsr305")) + fatJarContents(commonDep("io.javaslang", "javaslang")) + fatJarContents(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } + + proguardLibraryJars(files(firstFromJavaHomeThatExists("jre/lib/rt.jar", "../Classes/classes.jar"), + firstFromJavaHomeThatExists("jre/lib/jsse.jar", "../Classes/jsse.jar"), + toolsJar())) + proguardLibraryJars(projectDist(":kotlin-stdlib")) + proguardLibraryJars(projectDist(":kotlin-script-runtime")) + proguardLibraryJars(projectDist(":kotlin-reflect")) + proguardLibraryJars(projectDist(":kotlin-scripting-common")) + proguardLibraryJars(projectDist(":kotlin-scripting-jvm")) + + compile(project(":kotlin-stdlib")) + compile(project(":kotlin-script-runtime")) + compile(project(":kotlin-reflect")) + fatJarContents(intellijCoreDep()) { includeJars("intellij-core") } + fatJarContents(intellijDep()) { includeIntellijCoreJarDependencies(project, { !(it.startsWith("jdom") || it.startsWith("log4j")) }) } + fatJarContents(intellijDep()) { includeJars("jna-platform") } + fatJarContentsStripServices(intellijDep("jps-standalone")) { includeJars("jps-model") } + fatJarContentsStripMetadata(intellijDep()) { includeJars("oromatcher", "jdom", "log4j") } +} + + +val packCompiler by task { + configurations = listOf(fatJar) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + destinationDir = File(buildDir, "libs") + + setupPublicJar("before-proguard") + from(fatJarContents) + afterEvaluate { + fatJarContentsStripServices.files.forEach { from(zipTree(it)) { exclude("META-INF/services/**") } } + fatJarContentsStripMetadata.files.forEach { from(zipTree(it)) { exclude("META-INF/jb/** META-INF/LICENSE") } } + } + + manifest.attributes.put("Class-Path", compilerManifestClassPath) + manifest.attributes.put("Main-Class", "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler") +} + +val proguard by task { + dependsOn(packCompiler) + configuration("$rootDir/compiler/compiler.pro") + + val outputJar = File(buildDir, "libs", "$compilerBaseName-after-proguard.jar") + + inputs.files(packCompiler.outputs.files.singleFile) + outputs.file(outputJar) + + // TODO: remove after dropping compatibility with ant build + doFirst { + System.setProperty("kotlin-compiler-jar-before-shrink", packCompiler.outputs.files.singleFile.canonicalPath) + System.setProperty("kotlin-compiler-jar", outputJar.canonicalPath) + } + + libraryjars(mapOf("filter" to "!META-INF/versions/**"), proguardLibraryJars) + printconfiguration("$buildDir/compiler.pro.dump") +} + +noDefaultJar() + +val pack = if (shrink) proguard else packCompiler + +dist(targetName = compilerBaseName + ".jar", + fromTask = pack) + +runtimeJarArtifactBy(pack, pack.outputs.files.singleFile) { + name = compilerBaseName + classifier = "" +} +sourcesJar { + from(fatSourcesJarContents) +} +javadocJar() + +publish() + diff --git a/ultimate/build.gradle.kts b/ultimate/build.gradle.kts index 157de847646..c49d5324a0e 100644 --- a/ultimate/build.gradle.kts +++ b/ultimate/build.gradle.kts @@ -40,7 +40,7 @@ dependencies { if (intellijUltimateEnabled) { compileOnly(intellijUltimatePluginDep("NodeJS")) - compileOnly(intellijUltimateDep()) { includeJars("annotations", "trove4j", "openapi", "idea", "util", "jdom") } + 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")) @@ -66,7 +66,7 @@ dependencies { testCompile(projectTests(":generators:test-generator")) testCompile(commonDep("junit:junit")) if (intellijUltimateEnabled) { - testCompileOnly(intellijUltimateDep()) { includeJars("gson", "annotations", "trove4j", "openapi", "idea", "util", "jdom", rootProject = rootProject) } + 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 } @@ -89,6 +89,7 @@ dependencies { 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")) diff --git a/ultimate/build.gradle.kts.173 b/ultimate/build.gradle.kts.173 new file mode 100644 index 00000000000..157de847646 --- /dev/null +++ b/ultimate/build.gradle.kts.173 @@ -0,0 +1,206 @@ + +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", "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("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")) + + 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") diff --git a/ultimate/resources/META-INF/kotlin-spring.xml b/ultimate/resources/META-INF/kotlin-spring.xml index 64e157db101..10afa36b601 100644 --- a/ultimate/resources/META-INF/kotlin-spring.xml +++ b/ultimate/resources/META-INF/kotlin-spring.xml @@ -3,16 +3,8 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/KotlinFinalClassOrFunSpringInspection.kt b/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/KotlinFinalClassOrFunSpringInspection.kt.173 similarity index 100% rename from ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/KotlinFinalClassOrFunSpringInspection.kt rename to ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/KotlinFinalClassOrFunSpringInspection.kt.173 diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt b/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt.173 similarity index 100% rename from ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt rename to ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt.173 diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/lineMarking/KotlinSpringClassAnnotator.kt b/ultimate/src/org/jetbrains/kotlin/idea/spring/lineMarking/KotlinSpringClassAnnotator.kt.173 similarity index 100% rename from ultimate/src/org/jetbrains/kotlin/idea/spring/lineMarking/KotlinSpringClassAnnotator.kt rename to ultimate/src/org/jetbrains/kotlin/idea/spring/lineMarking/KotlinSpringClassAnnotator.kt.173 diff --git a/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test b/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test index f3e24f5d25c..1170f2a27be 100644 --- a/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test +++ b/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test @@ -1,4 +1,4 @@ -// INSPECTION_CLASS: org.jetbrains.kotlin.idea.spring.inspections.SpringKotlinAutowiringInspection +// INSPECTION_CLASS: com.intellij.spring.model.highlighting.autowire.SpringUastInjectionPointsAutowiringInspection // FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension // CONFIGURE_SPRING_FILE_SET // RUNTIME_WITH_FULL_JDK \ No newline at end of file diff --git a/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test.173 b/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test.173 new file mode 100644 index 00000000000..f3e24f5d25c --- /dev/null +++ b/ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test.173 @@ -0,0 +1,4 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.spring.inspections.SpringKotlinAutowiringInspection +// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension +// CONFIGURE_SPRING_FILE_SET +// RUNTIME_WITH_FULL_JDK \ No newline at end of file diff --git a/ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/expected.xml b/ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/expected.xml.173 similarity index 100% rename from ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/expected.xml rename to ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/expected.xml.173 diff --git a/ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test b/ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test.173 similarity index 100% rename from ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test rename to ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test.173 diff --git a/ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/test.kt b/ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/test.kt.173 similarity index 100% rename from ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/test.kt rename to ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/test.kt.173 diff --git a/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection b/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection index e188cf5be38..4b2e63563aa 100644 --- a/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection +++ b/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection @@ -1 +1 @@ -org.jetbrains.kotlin.idea.spring.inspections.SpringKotlinAutowiringInspection \ No newline at end of file +com.intellij.spring.model.highlighting.autowire.SpringUastInjectionPointsAutowiringInspection \ No newline at end of file diff --git a/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection.173 b/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection.173 new file mode 100644 index 00000000000..e188cf5be38 --- /dev/null +++ b/ultimate/testData/quickFixes/spring/addQualifierAnnotation/.inspection.173 @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.spring.inspections.SpringKotlinAutowiringInspection \ No newline at end of file diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/.inspection b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/.inspection.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/.inspection rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/.inspection.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt.after.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt.after.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt.after.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt.after.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt.after.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt.after.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt.173 diff --git a/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt.after b/ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt.after.173 similarity index 100% rename from ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt.after rename to ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt.after.173 diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java index 4c08ade96e9..4855646f192 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.findUsages; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java.173 new file mode 100644 index 00000000000..4c08ade96e9 --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/findUsages/SpringFindUsagesTestGenerated.java.173 @@ -0,0 +1,62 @@ +/* + * 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.spring.tests.findUsages; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/findUsages") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringFindUsagesTestGenerated extends AbstractSpringFindUsagesTest { + public void testAllFilesPresentInFindUsages() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/spring/core/findUsages"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("classXml.kt") + public void testClassXml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/findUsages/classXml.kt"); + doTest(fileName); + } + + @TestMetadata("primaryConstructorArgXml.kt") + public void testPrimaryConstructorArgXml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/findUsages/primaryConstructorArgXml.kt"); + doTest(fileName); + } + + @TestMetadata("propertyXml.kt") + public void testPropertyXml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/findUsages/propertyXml.kt"); + doTest(fileName); + } + + @TestMetadata("setterFunXml.kt") + public void testSetterFunXml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/findUsages/setterFunXml.kt"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java index 45d599af15c..3627fb85ef3 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.generate; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java.173 new file mode 100644 index 00000000000..45d599af15c --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/generate/GenerateSpringDependencyActionTestGenerated.java.173 @@ -0,0 +1,116 @@ +/* + * 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.spring.tests.generate; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/generate") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class GenerateSpringDependencyActionTestGenerated extends AbstractGenerateSpringDependencyActionTest { + public void testAllFilesPresentInGenerate() throws Exception { + KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("ultimate/testData/spring/core/generate"), Pattern.compile("^([\\w]+)\\.kt$"), TargetBackend.ANY); + } + + @TestMetadata("autowiredDependencies/duplicatingPropertyAnnotationConfig.kt") + public void testAutowiredDependencies_DuplicatingPropertyAnnotationConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/duplicatingPropertyAnnotationConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/duplicatingPropertyXmlConfig.kt") + public void testAutowiredDependencies_DuplicatingPropertyXmlConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/duplicatingPropertyXmlConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/multiplePropertiesAnnotationConfig.kt") + public void testAutowiredDependencies_MultiplePropertiesAnnotationConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/multiplePropertiesAnnotationConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/multiplePropertiesXmlConfig.kt") + public void testAutowiredDependencies_MultiplePropertiesXmlConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/multiplePropertiesXmlConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/propertyWithQualifierAnnotationConfig.kt") + public void testAutowiredDependencies_PropertyWithQualifierAnnotationConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/propertyWithQualifierAnnotationConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/propertyWithQualifierXmlConfig.kt") + public void testAutowiredDependencies_PropertyWithQualifierXmlConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/propertyWithQualifierXmlConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/singlePropertyAnnotationConfig.kt") + public void testAutowiredDependencies_SinglePropertyAnnotationConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/singlePropertyAnnotationConfig.kt"); + doTest(fileName); + } + + @TestMetadata("autowiredDependencies/singlePropertyXmlConfig.kt") + public void testAutowiredDependencies_SinglePropertyXmlConfig() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/autowiredDependencies/singlePropertyXmlConfig.kt"); + doTest(fileName); + } + + @TestMetadata("beanDependenciesByXml/firstConstructor.kt") + public void testBeanDependenciesByXml_FirstConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/beanDependenciesByXml/firstConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("beanDependenciesByXml/primaryConstructorAddParam.kt") + public void testBeanDependenciesByXml_PrimaryConstructorAddParam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/beanDependenciesByXml/primaryConstructorAddParam.kt"); + doTest(fileName); + } + + @TestMetadata("beanDependenciesByXml/property.kt") + public void testBeanDependenciesByXml_Property() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/beanDependenciesByXml/property.kt"); + doTest(fileName); + } + + @TestMetadata("beanDependenciesByXml/secondaryConstructorAddParam.kt") + public void testBeanDependenciesByXml_SecondaryConstructorAddParam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/beanDependenciesByXml/secondaryConstructorAddParam.kt"); + doTest(fileName); + } + + @TestMetadata("beanDependenciesByXml/setter.kt") + public void testBeanDependenciesByXml_Setter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/generate/beanDependenciesByXml/setter.kt"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt index 2f988846013..431476f12a1 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt @@ -28,7 +28,6 @@ import junit.framework.AssertionFailedError import org.jetbrains.kotlin.compatibility.projectDisposableEx import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.jsonUtils.getString -import org.jetbrains.kotlin.idea.spring.lineMarking.KotlinSpringClassAnnotator import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase @@ -43,10 +42,6 @@ abstract class AbstractSpringClassAnnotatorTest : KotlinLightCodeInsightFixtureT override fun setUp() { super.setUp() TestFixtureExtension.loadFixture(myModule) - Assert.assertTrue("Kotlin-ultimate service was not found, make sure that " + - "is replaced in `plugin.xml` with data from `ultimate-plugin.xml`", - LineMarkerProviders.INSTANCE.allForLanguage(KotlinLanguage.INSTANCE).any { it is KotlinSpringClassAnnotator } - ) } protected fun doTest(path: String) { diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt.173 new file mode 100644 index 00000000000..2f988846013 --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/AbstractSpringClassAnnotatorTest.kt.173 @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2016 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.spring.tests.gutter + +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.intellij.codeInsight.daemon.LineMarkerProviders +import com.intellij.openapi.util.io.FileUtil +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import icons.SpringApiIcons +import junit.framework.Assert +import junit.framework.AssertionFailedError +import org.jetbrains.kotlin.compatibility.projectDisposableEx +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.jsonUtils.getString +import org.jetbrains.kotlin.idea.spring.lineMarking.KotlinSpringClassAnnotator +import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.test.TestFixtureExtension +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +abstract class AbstractSpringClassAnnotatorTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST + + override fun setUp() { + super.setUp() + TestFixtureExtension.loadFixture(myModule) + Assert.assertTrue("Kotlin-ultimate service was not found, make sure that " + + "is replaced in `plugin.xml` with data from `ultimate-plugin.xml`", + LineMarkerProviders.INSTANCE.allForLanguage(KotlinLanguage.INSTANCE).any { it is KotlinSpringClassAnnotator } + ) + } + + protected fun doTest(path: String) { + val configFilePath = "${KotlinTestUtils.getHomeDirectory()}/$path" + val configFile = File(configFilePath) + val testRoot = configFile.parentFile + + val config = JsonParser().parse(FileUtil.loadFile(configFile, true)) as JsonObject + + PluginTestCaseBase.addJdk(myFixture.projectDisposableEx, PluginTestCaseBase::mockJdk) + + val withRuntime = config["withRuntime"]?.asBoolean ?: false + if (withRuntime) { + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk()) + } + + try { + val springConfigFiles = (config["springConfig"] as JsonArray).map { it.asString } + myFixture.testDataPath = testRoot.absolutePath + for (file in testRoot.listFiles()) { + val name = file.name + if (file.isDirectory) myFixture.copyDirectoryToProject(name, name) else myFixture.configureByFile(name) + } + TestFixtureExtension.getFixture()!!.configureFileSet(myFixture, springConfigFiles) + + val fileName = config.getString("file") + val iconName = config.getString("icon") + val icon = SpringApiIcons.Gutter::class.java.getField(iconName)[null] + + val gutter = myFixture.findGutter(fileName) ?: throw AssertionError("no gutter for '$fileName'") + val gutterMark = gutter.let { + if (it.icon == icon) it + else myFixture.findGuttersAtCaret().let { gutters -> + gutters.firstOrNull() { it.icon == icon } + ?: throw AssertionFailedError("no $icon in gutters: ${gutters.map { it.icon }}") + } + } + + val tooltip = config.getString("tooltip") + Assert.assertEquals(tooltip, gutterMark.tooltipText) + + val naming = config.getString("naming") + val targets = (config["targets"] as JsonArray).map { it.asString } + when (naming) { + "bean" -> checkBeanGutterTargets(gutterMark, targets) + "property" -> checkBeanPropertyTargets(gutterMark, targets) + "generic" -> checkPsiElementGutterTargets(gutterMark, targets) + else -> error("Unexpected naming: $naming") + } + } + finally { + if (withRuntime) { + ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk()) + } + } + } + + override fun tearDown() { + TestFixtureExtension.unloadFixture() + super.tearDown() + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java index 49869c1ab6d..6c22ae6017c 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.gutter; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java.173 new file mode 100644 index 00000000000..49869c1ab6d --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/gutter/SpringClassAnnotatorTestGenerated.java.173 @@ -0,0 +1,158 @@ +/* + * 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.spring.tests.gutter; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/gutter") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringClassAnnotatorTestGenerated extends AbstractSpringClassAnnotatorTest { + public void testAllFilesPresentInGutter() throws Exception { + KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("ultimate/testData/spring/core/gutter"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY); + } + + @TestMetadata("autowiredBeanCandidates/autowiredBeanCandidates.test") + public void testAutowiredBeanCandidates_AutowiredBeanCandidates() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/autowiredBeanCandidates/autowiredBeanCandidates.test"); + doTest(fileName); + } + + @TestMetadata("autowiredConstructor/autowiredConstructor.test") + public void testAutowiredConstructor_AutowiredConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/autowiredConstructor/autowiredConstructor.test"); + doTest(fileName); + } + + @TestMetadata("autowiredProperty/autowiredProperty.test") + public void testAutowiredProperty_AutowiredProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/autowiredProperty/autowiredProperty.test"); + doTest(fileName); + } + + @TestMetadata("autowiredSetter/autowiredSetter.test") + public void testAutowiredSetter_AutowiredSetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/autowiredSetter/autowiredSetter.test"); + doTest(fileName); + } + + @TestMetadata("classGutter/classGutter.test") + public void testClassGutter_ClassGutter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/classGutter/classGutter.test"); + doTest(fileName); + } + + @TestMetadata("classGutterAbstractShowMappedInheritors/classGutterAbstractShowMappedInheritors.test") + public void testClassGutterAbstractShowMappedInheritors_ClassGutterAbstractShowMappedInheritors() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/classGutterAbstractShowMappedInheritors/classGutterAbstractShowMappedInheritors.test"); + doTest(fileName); + } + + @TestMetadata("componentScan/componentScan.test") + public void testComponentScan_ComponentScan() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/componentScan/componentScan.test"); + doTest(fileName); + } + + @TestMetadata("componentScanWithBasePackageClasses/componentScanWithBasePackageClasses.test") + public void testComponentScanWithBasePackageClasses_ComponentScanWithBasePackageClasses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/componentScanWithBasePackageClasses/componentScanWithBasePackageClasses.test"); + doTest(fileName); + } + + @TestMetadata("componentScanWithBasePackages/componentScanWithBasePackages.test") + public void testComponentScanWithBasePackages_ComponentScanWithBasePackages() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/componentScanWithBasePackages/componentScanWithBasePackages.test"); + doTest(fileName); + } + + @TestMetadata("contextBeanInjectionPoints/contextBeanInjectionPoints.test") + public void testContextBeanInjectionPoints_ContextBeanInjectionPoints() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/contextBeanInjectionPoints/contextBeanInjectionPoints.test"); + doTest(fileName); + } + + @TestMetadata("contextBeanWithQualifierInjectionPoints/contextBeanWithQualifierInjectionPoints.test") + public void testContextBeanWithQualifierInjectionPoints_ContextBeanWithQualifierInjectionPoints() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/contextBeanWithQualifierInjectionPoints/contextBeanWithQualifierInjectionPoints.test"); + doTest(fileName); + } + + @TestMetadata("importConfigClasses/importConfigClasses.test") + public void testImportConfigClasses_ImportConfigClasses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/importConfigClasses/importConfigClasses.test"); + doTest(fileName); + } + + @TestMetadata("innerBean/innerBean.test") + public void testInnerBean_InnerBean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/innerBean/innerBean.test"); + doTest(fileName); + } + + @TestMetadata("methodTypeDefaultInitMethod/methodTypeDefaultInitMethod.test") + public void testMethodTypeDefaultInitMethod_MethodTypeDefaultInitMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/methodTypeDefaultInitMethod/methodTypeDefaultInitMethod.test"); + doTest(fileName); + } + + @TestMetadata("methodTypeFactory/methodTypeFactory.test") + public void testMethodTypeFactory_MethodTypeFactory() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/methodTypeFactory/methodTypeFactory.test"); + doTest(fileName); + } + + @TestMetadata("methodTypeInitMethod/methodTypeInitMethod.test") + public void testMethodTypeInitMethod_MethodTypeInitMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/methodTypeInitMethod/methodTypeInitMethod.test"); + doTest(fileName); + } + + @TestMetadata("methodTypeMultiple/methodTypeMultiple.test") + public void testMethodTypeMultiple_MethodTypeMultiple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/methodTypeMultiple/methodTypeMultiple.test"); + doTest(fileName); + } + + @TestMetadata("propertyGutterForProperty/propertyGutterForProperty.test") + public void testPropertyGutterForProperty_PropertyGutterForProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/propertyGutterForProperty/propertyGutterForProperty.test"); + doTest(fileName); + } + + @TestMetadata("propertyGutterForSetter/propertyGutterForSetter.test") + public void testPropertyGutterForSetter_PropertyGutterForSetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/propertyGutterForSetter/propertyGutterForSetter.test"); + doTest(fileName); + } + + @TestMetadata("resourceGutter/resourceGutter.test") + public void testResourceGutter_ResourceGutter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/gutter/resourceGutter/resourceGutter.test"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java index 08602ca6931..f1c9e055d73 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.inspections; @@ -54,12 +43,6 @@ public class SpringInspectionTestGenerated extends AbstractSpringInspectionTest doTest(fileName); } - @TestMetadata("finalSpringAnnotatedDeclaration/inspectionData/inspections.test") - public void testFinalSpringAnnotatedDeclaration_inspectionData_Inspections_test() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test"); - doTest(fileName); - } - @TestMetadata("unconfiguredFacet/inspectionData/inspections.test") public void testUnconfiguredFacet_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/unconfiguredFacet/inspectionData/inspections.test"); diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java.173 new file mode 100644 index 00000000000..08602ca6931 --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/inspections/SpringInspectionTestGenerated.java.173 @@ -0,0 +1,68 @@ +/* + * 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.spring.tests.inspections; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/inspections/spring") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringInspectionTestGenerated extends AbstractSpringInspectionTest { + public void testAllFilesPresentInSpring() throws Exception { + KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("ultimate/testData/inspections/spring"), Pattern.compile("^(inspections\\.test)$"), TargetBackend.ANY); + } + + @TestMetadata("autowiredMembersInInvalidClass/inspectionData/inspections.test") + public void testAutowiredMembersInInvalidClass_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/autowiredMembersInInvalidClass/inspectionData/inspections.test"); + doTest(fileName); + } + + @TestMetadata("autowiring/inspectionData/inspections.test") + public void testAutowiring_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/autowiring/inspectionData/inspections.test"); + doTest(fileName); + } + + @TestMetadata("componentScan/inspectionData/inspections.test") + public void testComponentScan_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/componentScan/inspectionData/inspections.test"); + doTest(fileName); + } + + @TestMetadata("finalSpringAnnotatedDeclaration/inspectionData/inspections.test") + public void testFinalSpringAnnotatedDeclaration_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test"); + doTest(fileName); + } + + @TestMetadata("unconfiguredFacet/inspectionData/inspections.test") + public void testUnconfiguredFacet_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/inspections/spring/unconfiguredFacet/inspectionData/inspections.test"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java index d8f732072b8..81bf33789e2 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.quickfixes; @@ -50,55 +39,4 @@ public class SpringQuickFixTestGenerated extends AbstractSpringQuickFixTest { doTest(fileName); } } - - @TestMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class FinalSpringAnnotatedDeclaration extends AbstractSpringQuickFixTest { - public void testAllFilesPresentInFinalSpringAnnotatedDeclaration() throws Exception { - KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); - } - - @TestMetadata("classWithComponentRuntime.kt") - public void testClassWithComponentRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt"); - doTest(fileName); - } - - @TestMetadata("classWithConfigurationRuntime.kt") - public void testClassWithConfigurationRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt"); - doTest(fileName); - } - - @TestMetadata("classWithCustomConfigurationRuntime.kt") - public void testClassWithCustomConfigurationRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt"); - doTest(fileName); - } - - @TestMetadata("funWithBeanFinalClassRuntime.kt") - public void testFunWithBeanFinalClassRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt"); - doTest(fileName); - } - - @TestMetadata("funWithBeanOpenClassRuntime.kt") - public void testFunWithBeanOpenClassRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt"); - doTest(fileName); - } - - @TestMetadata("funWithCustomBeanFinalClassRuntime.kt") - public void testFunWithCustomBeanFinalClassRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt"); - doTest(fileName); - } - - @TestMetadata("funWithCustomBeanOpenClassRuntime.kt") - public void testFunWithCustomBeanOpenClassRuntime() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt"); - doTest(fileName); - } - } } diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java.173 new file mode 100644 index 00000000000..d8f732072b8 --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/quickfixes/SpringQuickFixTestGenerated.java.173 @@ -0,0 +1,104 @@ +/* + * 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.spring.tests.quickfixes; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/quickFixes/spring") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringQuickFixTestGenerated extends AbstractSpringQuickFixTest { + public void testAllFilesPresentInSpring() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/quickFixes/spring"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("ultimate/testData/quickFixes/spring/addQualifierAnnotation") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddQualifierAnnotation extends AbstractSpringQuickFixTest { + public void testAllFilesPresentInAddQualifierAnnotation() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/quickFixes/spring/addQualifierAnnotation"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("ambiguousBean.kt") + public void testAmbiguousBean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/addQualifierAnnotation/ambiguousBean.kt"); + doTest(fileName); + } + } + + @TestMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FinalSpringAnnotatedDeclaration extends AbstractSpringQuickFixTest { + public void testAllFilesPresentInFinalSpringAnnotatedDeclaration() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("classWithComponentRuntime.kt") + public void testClassWithComponentRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("classWithConfigurationRuntime.kt") + public void testClassWithConfigurationRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("classWithCustomConfigurationRuntime.kt") + public void testClassWithCustomConfigurationRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("funWithBeanFinalClassRuntime.kt") + public void testFunWithBeanFinalClassRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("funWithBeanOpenClassRuntime.kt") + public void testFunWithBeanOpenClassRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("funWithCustomBeanFinalClassRuntime.kt") + public void testFunWithCustomBeanFinalClassRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("funWithCustomBeanOpenClassRuntime.kt") + public void testFunWithCustomBeanOpenClassRuntime() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/quickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt"); + doTest(fileName); + } + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java index 0321dcf9a5e..6cff1b865fa 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.references; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java.173 new file mode 100644 index 00000000000..0321dcf9a5e --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionHandlerTestGenerated.java.173 @@ -0,0 +1,80 @@ +/* + * 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.spring.tests.references; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/references/completion/handler") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringReferenceCompletionHandlerTestGenerated extends AbstractSpringReferenceCompletionHandlerTest { + public void testAllFilesPresentInHandler() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/spring/core/references/completion/handler"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("packageReferenceEnter.kt") + public void testPackageReferenceEnter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/packageReferenceEnter.kt"); + doTest(fileName); + } + + @TestMetadata("packageReferenceTab.kt") + public void testPackageReferenceTab() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/packageReferenceTab.kt"); + doTest(fileName); + } + + @TestMetadata("qualifierReferenceEnter.kt") + public void testQualifierReferenceEnter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/qualifierReferenceEnter.kt"); + doTest(fileName); + } + + @TestMetadata("qualifierReferenceTab.kt") + public void testQualifierReferenceTab() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/qualifierReferenceTab.kt"); + doTest(fileName); + } + + @TestMetadata("scopeReference.kt") + public void testScopeReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/scopeReference.kt"); + doTest(fileName); + } + + @TestMetadata("springBeanReferenceEnter.kt") + public void testSpringBeanReferenceEnter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/springBeanReferenceEnter.kt"); + doTest(fileName); + } + + @TestMetadata("springBeanReferenceTab.kt") + public void testSpringBeanReferenceTab() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/handler/springBeanReferenceTab.kt"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java index 3338f78d7f4..7b2f8826ffb 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.references; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java.173 new file mode 100644 index 00000000000..3338f78d7f4 --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceCompletionTestGenerated.java.173 @@ -0,0 +1,80 @@ +/* + * 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.spring.tests.references; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/references/completion/variants") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringReferenceCompletionTestGenerated extends AbstractSpringReferenceCompletionTest { + public void testAllFilesPresentInVariants() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/spring/core/references/completion/variants"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("beanWithDefaultName.kt") + public void testBeanWithDefaultName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/beanWithDefaultName.kt"); + doTest(fileName); + } + + @TestMetadata("beanWithExplicitName.kt") + public void testBeanWithExplicitName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/beanWithExplicitName.kt"); + doTest(fileName); + } + + @TestMetadata("packageReference.kt") + public void testPackageReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/packageReference.kt"); + doTest(fileName); + } + + @TestMetadata("qualifierReference1Xml.kt") + public void testQualifierReference1Xml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/qualifierReference1Xml.kt"); + doTest(fileName); + } + + @TestMetadata("qualifierReference2Xml.kt") + public void testQualifierReference2Xml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/qualifierReference2Xml.kt"); + doTest(fileName); + } + + @TestMetadata("scopeReferenceXml.kt") + public void testScopeReferenceXml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/scopeReferenceXml.kt"); + doTest(fileName); + } + + @TestMetadata("springBeanReferenceXml.kt") + public void testSpringBeanReferenceXml() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/completion/variants/springBeanReferenceXml.kt"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java index e6ef23a195a..c7fe6d9bf16 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.references; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java.173 new file mode 100644 index 00000000000..e6ef23a195a --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/references/SpringReferenceNavigationTestGenerated.java.173 @@ -0,0 +1,92 @@ +/* + * 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.spring.tests.references; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/references/navigation") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringReferenceNavigationTestGenerated extends AbstractSpringReferenceNavigationTest { + public void testAllFilesPresentInNavigation() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("ultimate/testData/spring/core/references/navigation"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("fileReferenceInClasspathResource.kt") + public void testFileReferenceInClasspathResource() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/fileReferenceInClasspathResource.kt"); + doTest(fileName); + } + + @TestMetadata("fileReferenceInClasspathXmlContext.kt") + public void testFileReferenceInClasspathXmlContext() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/fileReferenceInClasspathXmlContext.kt"); + doTest(fileName); + } + + @TestMetadata("packageReferenceInComponentScan.kt") + public void testPackageReferenceInComponentScan() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/packageReferenceInComponentScan.kt"); + doTest(fileName); + } + + @TestMetadata("qualifierReference.kt") + public void testQualifierReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/qualifierReference.kt"); + doTest(fileName); + } + + @TestMetadata("scopeReference.kt") + public void testScopeReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/scopeReference.kt"); + doTest(fileName); + } + + @TestMetadata("springBeanRefInFactoryContainsBean.kt") + public void testSpringBeanRefInFactoryContainsBean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/springBeanRefInFactoryContainsBean.kt"); + doTest(fileName); + } + + @TestMetadata("springBeanRefInFactoryGetBean.kt") + public void testSpringBeanRefInFactoryGetBean() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/springBeanRefInFactoryGetBean.kt"); + doTest(fileName); + } + + @TestMetadata("springBeanRefInResource.kt") + public void testSpringBeanRefInResource() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/springBeanRefInResource.kt"); + doTest(fileName); + } + + @TestMetadata("springFactoryBeanRefInResource.kt") + public void testSpringFactoryBeanRefInResource() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/references/navigation/springFactoryBeanRefInResource.kt"); + doTest(fileName); + } +} diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java index ad444b28264..2325bb56f9b 100644 --- a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java @@ -1,17 +1,6 @@ /* - * 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. + * 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.spring.tests.rename; diff --git a/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java.173 b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java.173 new file mode 100644 index 00000000000..ad444b28264 --- /dev/null +++ b/ultimate/tests/org/jetbrains/kotlin/idea/spring/tests/rename/SpringRenameTestGenerated.java.173 @@ -0,0 +1,176 @@ +/* + * 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.spring.tests.rename; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("ultimate/testData/spring/core/rename") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class SpringRenameTestGenerated extends AbstractSpringRenameTest { + public void testAllFilesPresentInRename() throws Exception { + KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("ultimate/testData/spring/core/rename"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY); + } + + @TestMetadata("annotationArgBySpELRefInXMLConf/annotationArgBySpELRefInXMLConf.test") + public void testAnnotationArgBySpELRefInXMLConf_AnnotationArgBySpELRefInXMLConf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/annotationArgBySpELRefInXMLConf/annotationArgBySpELRefInXMLConf.test"); + doTest(fileName); + } + + @TestMetadata("classWithXmlRefs/classWithXmlRef.test") + public void testClassWithXmlRefs_ClassWithXmlRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/classWithXmlRefs/classWithXmlRef.test"); + doTest(fileName); + } + + @TestMetadata("classWithXmlRefsByRef/classWithXmlRefByRef.test") + public void testClassWithXmlRefsByRef_ClassWithXmlRefByRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/classWithXmlRefsByRef/classWithXmlRefByRef.test"); + doTest(fileName); + } + + @TestMetadata("factoryMethodParam/factoryMethodParam.test") + public void testFactoryMethodParam_FactoryMethodParam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/factoryMethodParam/factoryMethodParam.test"); + doTest(fileName); + } + + @TestMetadata("factoryMethodParamByXmlRef/factoryMethodParamByXmlRef.test") + public void testFactoryMethodParamByXmlRef_FactoryMethodParamByXmlRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/factoryMethodParamByXmlRef/factoryMethodParamByXmlRef.test"); + doTest(fileName); + } + + @TestMetadata("isPropertyWithXmlRefsBySpelRef/isPropertyWithXmlRefBySpelRef.test") + public void testIsPropertyWithXmlRefsBySpelRef_IsPropertyWithXmlRefBySpelRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/isPropertyWithXmlRefsBySpelRef/isPropertyWithXmlRefBySpelRef.test"); + doTest(fileName); + } + + @TestMetadata("javaSpelRefToJava/javaSpelRefToJava.test") + public void testJavaSpelRefToJava_JavaSpelRefToJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/javaSpelRefToJava/javaSpelRefToJava.test"); + doTest(fileName); + } + + @TestMetadata("javaSpelRefToJavaAnnotated/javaSpelRefToJavaAnnotated.test") + public void testJavaSpelRefToJavaAnnotated_JavaSpelRefToJavaAnnotated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/javaSpelRefToJavaAnnotated/javaSpelRefToJavaAnnotated.test"); + doTest(fileName); + } + + @TestMetadata("javaSpelRefToKt/javaSpelRefToKt.test") + public void testJavaSpelRefToKt_JavaSpelRefToKt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/javaSpelRefToKt/javaSpelRefToKt.test"); + doTest(fileName); + } + + @TestMetadata("javaSpelRefToKtAnnotated/javaSpelRefToKtAnnotated.test") + public void testJavaSpelRefToKtAnnotated_JavaSpelRefToKtAnnotated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/javaSpelRefToKtAnnotated/javaSpelRefToKtAnnotated.test"); + doTest(fileName); + } + + @TestMetadata("ktSpelRefToJava/ktSpelRefToJava.test") + public void testKtSpelRefToJava_KtSpelRefToJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/ktSpelRefToJava/ktSpelRefToJava.test"); + doTest(fileName); + } + + @TestMetadata("ktSpelRefToJavaAnnotated/ktSpelRefToJavaAnnotated.test") + public void testKtSpelRefToJavaAnnotated_KtSpelRefToJavaAnnotated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/ktSpelRefToJavaAnnotated/ktSpelRefToJavaAnnotated.test"); + doTest(fileName); + } + + @TestMetadata("ktSpelRefToKt/ktSpelRefToKt.test") + public void testKtSpelRefToKt_KtSpelRefToKt() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/ktSpelRefToKt/ktSpelRefToKt.test"); + doTest(fileName); + } + + @TestMetadata("ktSpelRefToKtAnnotated/ktSpelRefToKtAnnotated.test") + public void testKtSpelRefToKtAnnotated_KtSpelRefToKtAnnotated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/ktSpelRefToKtAnnotated/ktSpelRefToKtAnnotated.test"); + doTest(fileName); + } + + @TestMetadata("parameterWithXmlRefsBySpelRef/parameterWithXmlRefBySpelRef.test") + public void testParameterWithXmlRefsBySpelRef_ParameterWithXmlRefBySpelRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/parameterWithXmlRefsBySpelRef/parameterWithXmlRefBySpelRef.test"); + doTest(fileName); + } + + @TestMetadata("primaryConstructorArgWithXmlRefs/primaryConstructorArgWithXmlRef.test") + public void testPrimaryConstructorArgWithXmlRefs_PrimaryConstructorArgWithXmlRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/primaryConstructorArgWithXmlRefs/primaryConstructorArgWithXmlRef.test"); + doTest(fileName); + } + + @TestMetadata("primaryConstructorArgWithXmlRefsByRef1/primaryConstructorArgWithXmlRefByRef1.test") + public void testPrimaryConstructorArgWithXmlRefsByRef1_PrimaryConstructorArgWithXmlRefByRef1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/primaryConstructorArgWithXmlRefsByRef1/primaryConstructorArgWithXmlRefByRef1.test"); + doTest(fileName); + } + + @TestMetadata("primaryConstructorArgWithXmlRefsByRef2/primaryConstructorArgWithXmlRefByRef2.test") + public void testPrimaryConstructorArgWithXmlRefsByRef2_PrimaryConstructorArgWithXmlRefByRef2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/primaryConstructorArgWithXmlRefsByRef2/primaryConstructorArgWithXmlRefByRef2.test"); + doTest(fileName); + } + + @TestMetadata("propertyWithXmlRefs/propertyWithXmlRef.test") + public void testPropertyWithXmlRefs_PropertyWithXmlRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/propertyWithXmlRefs/propertyWithXmlRef.test"); + doTest(fileName); + } + + @TestMetadata("propertyWithXmlRefsByRef/propertyWithXmlRefByRef.test") + public void testPropertyWithXmlRefsByRef_PropertyWithXmlRefByRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/propertyWithXmlRefsByRef/propertyWithXmlRefByRef.test"); + doTest(fileName); + } + + @TestMetadata("propertyWithXmlRefsBySpelRef/propertyWithXmlRefBySpelRef.test") + public void testPropertyWithXmlRefsBySpelRef_PropertyWithXmlRefBySpelRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/propertyWithXmlRefsBySpelRef/propertyWithXmlRefBySpelRef.test"); + doTest(fileName); + } + + @TestMetadata("setterFunWithXmlRefs/setterFunWithXmlRef.test") + public void testSetterFunWithXmlRefs_SetterFunWithXmlRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/setterFunWithXmlRefs/setterFunWithXmlRef.test"); + doTest(fileName); + } + + @TestMetadata("setterFunWithXmlRefsByRef/setterFunWithXmlRefByRef.test") + public void testSetterFunWithXmlRefsByRef_SetterFunWithXmlRefByRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/setterFunWithXmlRefsByRef/setterFunWithXmlRefByRef.test"); + doTest(fileName); + } +} diff --git a/versions.gradle.kts b/versions.gradle.kts index 8833322f05b..b0719e7f3c6 100644 --- a/versions.gradle.kts +++ b/versions.gradle.kts @@ -1,7 +1,7 @@ -extra["versions.intellijSdk"] = "173.4674.33" +extra["versions.intellijSdk"] = "181.4203.6" extra["versions.androidBuildTools"] = "r23.0.1" -extra["versions.idea.NodeJS"] = "172.3757.32" +extra["versions.idea.NodeJS"] = "181.3494.12" //extra["versions.androidStudioRelease"] = "3.1.0.5" //extra["versions.androidStudioBuild"] = "173.4506631" @@ -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" } diff --git a/versions.gradle.kts.173 b/versions.gradle.kts.173 new file mode 100644 index 00000000000..8833322f05b --- /dev/null +++ b/versions.gradle.kts.173 @@ -0,0 +1,107 @@ + +extra["versions.intellijSdk"] = "173.4674.33" +extra["versions.androidBuildTools"] = "r23.0.1" +extra["versions.idea.NodeJS"] = "172.3757.32" +//extra["versions.androidStudioRelease"] = "3.1.0.5" +//extra["versions.androidStudioBuild"] = "173.4506631" + +val gradleJars = listOf( + "gradle-api", + "gradle-tooling-api", + "gradle-base-services", + "gradle-wrapper", + "gradle-core", + "gradle-base-services-groovy" +) + +val androidStudioVersion = if (extra.has("versions.androidStudioRelease")) + extra["versions.androidStudioRelease"]?.toString()?.replace(".", "")?.substring(0, 2) +else + null + +val platform = androidStudioVersion?.let { "AS" + it } + ?: extra["versions.intellijSdk"].toString().substringBefore('.') + +when (platform) { + "181" -> { + extra["versions.jar.guava"] = "21.0" + extra["versions.jar.groovy-all"] = "2.4.12" + extra["versions.jar.lombok-ast"] = "0.2.3" + extra["versions.jar.swingx-core"] = "1.6.2-2" + extra["versions.jar.kxml2"] = "2.3.0" + extra["versions.jar.streamex"] = "0.6.5" + extra["versions.jar.gson"] = "2.8.2" + for (jar in gradleJars) { + extra["versions.jar.$jar"] = "4.4" + } + } + "173" -> { + extra["versions.jar.guava"] = "21.0" + extra["versions.jar.groovy-all"] = "2.4.12" + extra["versions.jar.lombok-ast"] = "0.2.3" + extra["versions.jar.swingx-core"] = "1.6.2" + extra["versions.jar.kxml2"] = "2.3.0" + extra["versions.jar.streamex"] = "0.6.5" + extra["versions.jar.gson"] = "2.8.2" + for (jar in gradleJars) { + extra["versions.jar.$jar"] = "4.0" + } + extra["ignore.jar.lombok-ast-0.2.3"] = true + } + "172" -> { + extra["versions.jar.guava"] = "21.0" + extra["versions.jar.groovy-all"] = "2.4.6" + extra["versions.jar.lombok-ast"] = "0.2.3" + extra["versions.jar.swingx-core"] = "1.6.2" + extra["versions.jar.kxml2"] = "2.3.0" + extra["versions.jar.streamex"] = "0.6.2" + extra["versions.jar.gson"] = "2.5" + for (jar in gradleJars) { + extra["versions.jar.$jar"] = "3.5" + } + } + "171" -> { + extra["versions.jar.guava"] = "19.0" + extra["versions.jar.groovy-all"] = "2.4.6" + extra["versions.jar.lombok-ast"] = "0.2.3" + extra["versions.jar.swingx-core"] = "1.6.2" + extra["versions.jar.kxml2"] = "2.3.0" + extra["versions.jar.streamex"] = "0.6.2" + extra["versions.jar.gson"] = "2.5" + for (jar in gradleJars) { + extra["versions.jar.$jar"] = "3.3" + } + } + "AS30" -> { + extra["versions.jar.guava"] = "19.0" + extra["versions.jar.groovy-all"] = "2.4.6" + extra["versions.jar.lombok-ast"] = "0.2.3" + extra["versions.jar.swingx-core"] = "1.6.2" + extra["versions.jar.kxml2"] = "2.3.0" + extra["versions.jar.streamex"] = "0.6.2" + extra["versions.jar.gson"] = "2.5" + for (jar in gradleJars) { + extra["versions.jar.$jar"] = "3.5" + } + + extra["ignore.jar.common"] = true + } + "AS31" -> { + extra["versions.jar.guava"] = "21.0" + extra["versions.jar.groovy-all"] = "2.4.12" + extra["versions.jar.swingx-core"] = "1.6.2" + extra["versions.jar.kxml2"] = "2.3.0" + extra["versions.jar.streamex"] = "0.6.5" + extra["versions.jar.gson"] = "2.8.2" + for (jar in gradleJars) { + extra["versions.jar.$jar"] = "4.0" + } + + extra["ignore.jar.common"] = true + extra["ignore.jar.lombok-ast"] = true + } +} + +if (!extra.has("versions.androidStudioRelease")) { + extra["ignore.jar.android-base-common"] = true +}