Compare commits

...

1 Commits

Author SHA1 Message Date
Nicolay Mitropolsky
7df56bf397 ~~~~ switch 191 ~~~~ 2019-01-19 12:29:34 +03:00
29 changed files with 2247 additions and 46 deletions

View File

@@ -51,7 +51,11 @@ class MockExternalAnnotationsManager : ExternalAnnotationsManager() {
throw UnsupportedOperationException("not implemented")
}
override fun hasAnnotationRootsForFile(file: VirtualFile): Boolean {
throw UnsupportedOperationException("not implemented")
}
override fun hasAnnotationRootsForFile(file: VirtualFile): Boolean = false
override fun findDefaultConstructorExternalAnnotations(aClass: PsiClass, annotationFQN: String): List<PsiAnnotation> = emptyList()
override fun findDefaultConstructorExternalAnnotations(aClass: PsiClass): List<PsiAnnotation> = emptyList()
override fun findExternalAnnotations(listOwner: PsiModifierListOwner, annotationFQN: String): List<PsiAnnotation> = emptyList()
}

View File

@@ -0,0 +1,57 @@
/*
* 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.cli.jvm.compiler
import com.intellij.codeInsight.ExternalAnnotationsManager
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
class MockExternalAnnotationsManager : ExternalAnnotationsManager() {
override fun chooseAnnotationsPlace(element: PsiElement): AnnotationPlace? = null
override fun isExternalAnnotationWritable(listOwner: PsiModifierListOwner, annotationFQN: String): Boolean = false
override fun isExternalAnnotation(annotation: PsiAnnotation): Boolean = false
override fun findExternalAnnotationsFiles(listOwner: PsiModifierListOwner): List<PsiFile>? = null
override fun findExternalAnnotation(listOwner: PsiModifierListOwner, annotationFQN: String): PsiAnnotation? = null
override fun findExternalAnnotations(listOwner: PsiModifierListOwner): Array<out PsiAnnotation>? = null
override fun annotateExternally(
listOwner: PsiModifierListOwner,
annotationFQName: String,
fromFile: PsiFile,
value: Array<out PsiNameValuePair>?
) {
throw UnsupportedOperationException("not implemented")
}
override fun deannotate(listOwner: PsiModifierListOwner, annotationFQN: String): Boolean {
throw UnsupportedOperationException("not implemented")
}
override fun editExternalAnnotation(
listOwner: PsiModifierListOwner,
annotationFQN: String,
value: Array<out PsiNameValuePair>?
): Boolean {
throw UnsupportedOperationException("not implemented")
}
override fun hasAnnotationRootsForFile(file: VirtualFile): Boolean {
throw UnsupportedOperationException("not implemented")
}
}

View File

@@ -16,6 +16,7 @@ messages/**)
-dontnote **
-dontwarn com.intellij.util.ui.IsRetina*
-dontwarn com.intellij.util.ui.UIUtilities
-dontwarn com.intellij.util.RetinaImage*
-dontwarn apple.awt.*
-dontwarn dk.brics.automaton.*
@@ -196,7 +197,6 @@ messages/**)
-keep class org.jetbrains.org.objectweb.asm.tree.FieldNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.ParameterNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.TypeAnnotationNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.InsnList { *; }
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureReader { *; }
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureVisitor { *; }

250
compiler/compiler.pro.183 Normal file
View File

@@ -0,0 +1,250 @@
-injars '<kotlin-compiler-jar-before-shrink>'(
!org/apache/log4j/jmx/Agent*,
!org/apache/log4j/net/JMS*,
!org/apache/log4j/net/SMTP*,
!org/apache/log4j/or/jms/MessageRenderer*,
!org/jdom/xpath/Jaxen*,
!org/jline/builtins/ssh/**,
!org/mozilla/javascript/xml/impl/xmlbeans/**,
!net/sf/cglib/**,
!META-INF/maven**,
**.class,**.properties,**.kt,**.kotlin_*,**.jnilib,**.so,**.dll,**.txt,**.caps,
META-INF/services/**,META-INF/native/**,META-INF/extensions/**,META-INF/MANIFEST.MF,
messages/**)
-outjars '<kotlin-compiler-jar>'
-dontnote **
-dontwarn com.intellij.util.ui.IsRetina*
-dontwarn com.intellij.util.RetinaImage*
-dontwarn apple.awt.*
-dontwarn dk.brics.automaton.*
-dontwarn org.fusesource.**
-dontwarn org.imgscalr.Scalr**
-dontwarn org.xerial.snappy.SnappyBundleActivator
-dontwarn com.intellij.util.CompressionUtil
-dontwarn com.intellij.util.SnappyInitializer
-dontwarn com.intellij.util.SVGLoader
-dontwarn com.intellij.util.SVGLoader$MyTranscoder
-dontwarn net.sf.cglib.**
-dontwarn org.objectweb.asm.** # this is ASM3, the old version that we do not use
-dontwarn com.sun.jna.NativeString
-dontwarn com.sun.jna.WString
-dontwarn com.intellij.psi.util.PsiClassUtil
-dontwarn org.apache.hadoop.io.compress.*
-dontwarn com.google.j2objc.annotations.Weak
-dontwarn org.iq80.snappy.HadoopSnappyCodec$SnappyCompressionInputStream
-dontwarn org.iq80.snappy.HadoopSnappyCodec$SnappyCompressionOutputStream
-dontwarn com.google.common.util.concurrent.*
-dontwarn org.apache.xerces.dom.**
-dontwarn org.apache.xerces.util.**
-dontwarn org.w3c.dom.ElementTraversal
-dontwarn javaslang.match.annotation.Unapply
-dontwarn javaslang.match.annotation.Patterns
-dontwarn javaslang.*
-dontwarn com.google.errorprone.**
-dontwarn com.google.j2objc.**
-dontwarn javax.crypto.**
-dontwarn java.lang.invoke.MethodHandle
-dontwarn org.jline.builtins.Nano$Buffer
-dontwarn org.jetbrains.annotations.ReadOnly
-dontwarn org.jetbrains.annotations.Mutable
-dontwarn com.intellij.util.io.TarUtil
-dontwarn com.intellij.util.io.Compressor$Tar
# Nullability annotations used in Guava
-dontwarn org.checkerframework.checker.nullness.compatqual.NullableDecl
-dontwarn org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl
-dontwarn org.checkerframework.checker.nullness.qual.Nullable
-dontwarn org.checkerframework.checker.nullness.qual.MonotonicNonNull
# Depends on apache batick which has lots of dependencies
-dontwarn com.intellij.util.SVGLoader*
-dontwarn org.apache.batik.script.rhino.RhinoInterpreter
-dontwarn org.apache.batik.script.rhino.RhinoInterpreterFactory
-dontwarn org.jdom.xpath.jaxen.*
-dontwarn com.intellij.util.io.Decompressor*
-dontwarn org.w3c.dom.Location
-dontwarn org.w3c.dom.Window
#-libraryjars '<rtjar>'
#-libraryjars '<jssejar>'
#-libraryjars '<bootstrap.runtime>'
#-libraryjars '<bootstrap.reflect>'
#-libraryjars '<bootstrap.script.runtime>'
#-libraryjars '<tools.jar>'
-dontoptimize
-dontobfuscate
-keep class org.fusesource.** { *; }
-keep class com.sun.jna.** { *; }
-keep class org.jetbrains.annotations.** {
public protected *;
}
-keep class javax.inject.** {
public protected *;
}
-keep class org.jetbrains.kotlin.** {
public protected *;
}
-keep class org.jetbrains.kotlin.compiler.plugin.** {
public protected *;
}
-keep class org.jetbrains.kotlin.extensions.** {
public protected *;
}
-keep class org.jetbrains.kotlin.protobuf.** {
public protected *;
}
-keep class org.jetbrains.kotlin.container.** { *; }
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
-keep class org.jetbrains.kotlin.codegen.extensions.** {
public protected *;
}
-keepclassmembers class com.intellij.openapi.vfs.VirtualFile {
public protected *;
}
-keep class com.intellij.openapi.vfs.StandardFileSystems {
public static *;
}
# needed for jar cache cleanup in the gradle plugin and compile daemon
-keepclassmembers class com.intellij.openapi.vfs.impl.ZipHandler {
public static void clearFileAccessorCache();
}
-keep class jet.** {
public protected *;
}
-keep class com.intellij.psi.** {
public protected *;
}
# This is needed so that the platform code which parses XML wouldn't fail, see KT-16968
# This API is used from org.jdom.input.SAXBuilder via reflection.
-keep class org.jdom.input.JAXPParserFactory { public ** createParser(...); }
# Without this class PluginManagerCore.loadDescriptorFromJar fails
-keep class org.jdom.output.XMLOutputter { *; }
# for kdoc & dokka
-keep class com.intellij.openapi.util.TextRange { *; }
-keep class com.intellij.lang.impl.PsiBuilderImpl* {
public protected *;
}
-keep class com.intellij.openapi.util.text.StringHash { *; }
# for j2k
-keep class com.intellij.codeInsight.NullableNotNullManager { public protected *; }
# for gradle (see KT-12549)
-keep class com.intellij.lang.properties.charset.Native2AsciiCharsetProvider { *; }
# for kotlin-build-common (consider repacking compiler together with kotlin-build-common and remove this part afterwards)
-keep class com.intellij.util.io.IOUtil { public *; }
-keep class com.intellij.openapi.util.io.FileUtil { public *; }
-keep class com.intellij.util.SystemProperties { public *; }
-keep class com.intellij.util.containers.hash.LinkedHashMap { *; }
-keep class com.intellij.util.containers.ConcurrentIntObjectMap { *; }
-keep class com.intellij.util.containers.ComparatorUtil { *; }
-keep class com.intellij.util.io.PersistentHashMapValueStorage { *; }
-keep class com.intellij.util.io.PersistentHashMap { *; }
-keep class com.intellij.util.io.BooleanDataDescriptor { *; }
-keep class com.intellij.util.io.EnumeratorStringDescriptor { *; }
-keep class com.intellij.util.io.ExternalIntegerKeyDescriptor { *; }
-keep class com.intellij.util.containers.hash.EqualityPolicy { *; }
-keep class com.intellij.util.containers.hash.EqualityPolicy.* { *; }
-keep class com.intellij.util.containers.Interner { *; }
-keep class gnu.trove.TIntHashSet { *; }
-keep class gnu.trove.TIntIterator { *; }
-keep class org.iq80.snappy.SlowMemory { *; }
-keep class javaslang.match.PatternsProcessor { *; }
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * {
** toString();
** hashCode();
void start();
void stop();
void dispose();
}
-keep class org.jetbrains.org.objectweb.asm.tree.AnnotationNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.ClassNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.LocalVariableNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.MethodNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.FieldNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.ParameterNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.TypeAnnotationNode { *; }
-keep class org.jetbrains.org.objectweb.asm.tree.InsnList { *; }
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureReader { *; }
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureVisitor { *; }
-keep class org.jetbrains.org.objectweb.asm.Type {
public protected *;
}
-keepclassmembers class org.jetbrains.org.objectweb.asm.ClassReader {
*** SKIP_CODE;
*** SKIP_DEBUG;
*** SKIP_FRAMES;
}
-keepclassmembers class com.intellij.openapi.project.Project {
** getBasePath();
}
# for kotlin-android-extensions in maven
-keep class com.intellij.openapi.module.ModuleServiceManager { public *; }
# for building kotlin-build-common-test
-keep class org.jetbrains.kotlin.build.SerializationUtilsKt { *; }
# for tools.jar
-keep class com.sun.tools.javac.** { *; }
-keep class com.sun.source.** { *; }
# for coroutines
-keep class kotlinx.coroutines.** { *; }
# for webdemo
-keep class com.intellij.openapi.progress.ProgressManager { *; }
# for kapt
-keep class com.intellij.openapi.project.Project { *; }
-keepclassmembers class com.intellij.util.PathUtil {
public static java.lang.String getJarPathForClass(java.lang.Class);
}
-keepclassmembers class com.intellij.util.PathUtil {
public static java.lang.String getJarPathForClass(java.lang.Class);
}
# remove when KT-18563 would be fixed
-keep class org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt { *; }
-keep class net.jpountz.lz4.* { *; }
# used in LazyScriptDescriptor
-keep class org.jetbrains.kotlin.utils.addToStdlib.AddToStdlibKt { *; }

View File

@@ -1,6 +1,7 @@
versions.intellijSdk=183.5153.4
versions.intellijSdk=191-SNAPSHOT
versions.androidBuildTools=r23.0.1
versions.idea.NodeJS=181.3494.12
versions.jar.asm-all=7.0
versions.jar.guava=25.1-jre
versions.jar.groovy-all=2.4.15
versions.jar.lombok-ast=0.2.3
@@ -10,6 +11,5 @@ versions.jar.streamex=0.6.7
versions.jar.gson=2.8.5
versions.jar.oro=2.0.8
versions.jar.picocontainer=1.2
versions.jar.asm-all=7.0
ignore.jar.snappy-in-java=true
versions.gradle-api=4.5.1

View File

@@ -0,0 +1,15 @@
versions.intellijSdk=183.5153.4
versions.androidBuildTools=r23.0.1
versions.idea.NodeJS=181.3494.12
versions.jar.guava=25.1-jre
versions.jar.groovy-all=2.4.15
versions.jar.lombok-ast=0.2.3
versions.jar.swingx-core=1.6.2-2
versions.jar.kxml2=2.3.0
versions.jar.streamex=0.6.7
versions.jar.gson=2.8.5
versions.jar.oro=2.0.8
versions.jar.picocontainer=1.2
versions.jar.asm-all=7.0
ignore.jar.snappy-in-java=true
versions.gradle-api=4.5.1

View File

@@ -11,17 +11,14 @@ import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
sealed class KotlinResourceRootType(val isTest: Boolean) : JpsElementTypeBase<JavaResourceRootProperties>(),
JpsModuleSourceRootType<JavaResourceRootProperties>, KotlinRootType {
object Resource : KotlinResourceRootType(false)
object TestResource : KotlinResourceRootType(true)
sealed class KotlinResourceRootType() : JpsElementTypeBase<JavaResourceRootProperties>(),
JpsModuleSourceRootType<JavaResourceRootProperties> {
object Resource : KotlinResourceRootType()
object TestResource : KotlinResourceRootType() {
override fun isForTests() = true
}
override fun createDefaultProperties() =
JpsJavaExtensionService.getInstance().createResourceRootProperties("", false)
override fun isTestRoot() = isTest
override fun isForTests() = isTest
override fun equals(other: Any?) = if (super.equals(other)) true else isSameRootType(this, other)
}

View File

@@ -0,0 +1,27 @@
/*
* 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.config
import org.jetbrains.jps.model.ex.JpsElementTypeBase
import org.jetbrains.jps.model.java.JavaResourceRootProperties
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
sealed class KotlinResourceRootType(val isTest: Boolean) : JpsElementTypeBase<JavaResourceRootProperties>(),
JpsModuleSourceRootType<JavaResourceRootProperties>, KotlinRootType {
object Resource : KotlinResourceRootType(false)
object TestResource : KotlinResourceRootType(true)
override fun createDefaultProperties() =
JpsJavaExtensionService.getInstance().createResourceRootProperties("", false)
override fun isTestRoot() = isTest
override fun isForTests() = isTest
override fun equals(other: Any?) = if (super.equals(other)) true else isSameRootType(this, other)
}

View File

@@ -11,20 +11,17 @@ import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
sealed class KotlinSourceRootType(val isTest: Boolean) : JpsElementTypeBase<JavaSourceRootProperties>(), JpsModuleSourceRootType<JavaSourceRootProperties>, KotlinRootType {
object Source : KotlinSourceRootType(false)
object TestSource : KotlinSourceRootType(true)
sealed class KotlinSourceRootType() : JpsElementTypeBase<JavaSourceRootProperties>(), JpsModuleSourceRootType<JavaSourceRootProperties> {
object Source : KotlinSourceRootType()
object TestSource : KotlinSourceRootType() {
override fun isForTests() = true
}
override fun createDefaultProperties() = JpsJavaExtensionService.getInstance().createSourceRootProperties("")
companion object {
val ALL_SOURCES = setOf(Source, TestSource)
}
override fun isTestRoot() = isTest
override fun isForTests() = isTest
override fun equals(other: Any?) = if (super.equals(other)) true else isSameRootType(this, other)
}

View File

@@ -0,0 +1,30 @@
/*
* 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.config
import org.jetbrains.jps.model.ex.JpsElementTypeBase
import org.jetbrains.jps.model.java.JavaSourceRootProperties
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
sealed class KotlinSourceRootType(val isTest: Boolean) : JpsElementTypeBase<JavaSourceRootProperties>(), JpsModuleSourceRootType<JavaSourceRootProperties>, KotlinRootType {
object Source : KotlinSourceRootType(false)
object TestSource : KotlinSourceRootType(true)
override fun createDefaultProperties() = JpsJavaExtensionService.getInstance().createSourceRootProperties("")
companion object {
val ALL_SOURCES = setOf(Source, TestSource)
}
override fun isTestRoot() = isTest
override fun isForTests() = isTest
override fun equals(other: Any?) = if (super.equals(other)) true else isSameRootType(this, other)
}

View File

@@ -25,10 +25,10 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.*
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.concurrency.AsyncPromise
import org.jetbrains.idea.maven.importing.MavenImporter
import org.jetbrains.idea.maven.importing.MavenRootModelAdapter
import org.jetbrains.idea.maven.model.MavenPlugin
@@ -140,7 +140,7 @@ class KotlinMavenImporter : MavenImporter(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_
if (toBeDownloaded.isNotEmpty()) {
MavenProjectsManager.getInstance(module.project)
.scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncResult())
.scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncPromise())
}
}

View File

@@ -0,0 +1,422 @@
/*
* 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.*
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.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.maven.configuration.KotlinMavenConfigurator
import org.jetbrains.kotlin.idea.platform.tooling
import org.jetbrains.kotlin.platform.IdePlatform
import org.jetbrains.kotlin.platform.IdePlatformKind
import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind
import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
import java.util.*
interface MavenProjectImportHandler {
companion object : ProjectExtensionDescriptor<MavenProjectImportHandler>(
"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<MavenProject, String>,
postTasks: MutableList<MavenProjectsProcessorTask>
) {
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)?.tooling?.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<Library>()
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: IdePlatform<*, *>
): List<String> {
val arguments = platform.createArguments()
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<String>().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,
ExternalProjectSystemRegistry.MAVEN_EXTERNAL_SOURCE_ID
)
// TODO There should be a way to figure out the correct platform version
val platform = detectPlatform(mavenProject)?.defaultPlatform
kotlinFacet.configureFacet(compilerVersion, LanguageFeature.Coroutines.defaultState, platform, modifiableModelsProvider)
val facetSettings = kotlinFacet.configuration.settings
val configuredPlatform = kotlinFacet.configuration.settings.platform!!
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): IdePlatformKind<*>? {
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 -> JvmIdePlatformKind
PomFile.KotlinGoals.Js, PomFile.KotlinGoals.TestJs -> JsIdePlatformKind
PomFile.KotlinGoals.MetaData -> CommonIdePlatformKind
else -> null
}
}?.distinct()?.singleOrNull()
}
private fun detectPlatformByLibraries(mavenProject: MavenProject): IdePlatformKind<*>? {
for (kind in IdePlatformKind.ALL_KINDS) {
val mavenLibraryIds = kind.tooling.mavenLibraryIds
if (mavenLibraryIds.any { mavenProject.findDependencies(KOTLIN_PLUGIN_GROUP_ID, it).isNotEmpty() }) {
// TODO we could return here the correct version
return kind
}
}
return null
}
// 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<String, JpsModuleSourceRootType<*>>) {
// 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
val isNonJvmModule = mavenProject
.plugins
.asSequence()
.filter { it.isKotlinPlugin() }
.flatMap { it.executions.asSequence() }
.flatMap { it.goals.asSequence() }
.any { it in PomFile.KotlinGoals.CompileGoals && it !in PomFile.KotlinGoals.JvmGoals }
val prodSourceRootType: JpsModuleSourceRootType<*> = if (isNonJvmModule) KotlinSourceRootType.Source else JavaSourceRootType.SOURCE
val testSourceRootType: JpsModuleSourceRootType<*> = if (isNonJvmModule) KotlinSourceRootType.TestSource else JavaSourceRootType.TEST_SOURCE
for ((type, dir) in directories) {
if (rootModel.getSourceFolder(File(dir)) == null) {
val jpsType: JpsModuleSourceRootType<*> = when (type) {
SourceType.TEST -> testSourceRootType
SourceType.PROD -> prodSourceRootType
}
rootModel.addSourceFolder(dir, jpsType)
}
}
if (isNonJvmModule) {
mavenProject.sources.forEach { rootModel.addSourceFolder(it, KotlinSourceRootType.Source) }
mavenProject.testSources.forEach { rootModel.addSourceFolder(it, KotlinSourceRootType.TestSource) }
mavenProject.resources.forEach { rootModel.addSourceFolder(it.directory, KotlinResourceRootType.Resource) }
mavenProject.testResources.forEach { rootModel.addSourceFolder(it.directory, KotlinResourceRootType.TestResource) }
}
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<Pair<SourceType, String>> =
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.platform.isCommon) {
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).isCommon }
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<String> =
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(file = StoragePathMacros.MODULE_FILE))]
)
class KotlinImporterComponent : PersistentStateComponent<KotlinImporterComponent.State> {
class State(var directories: List<String> = ArrayList())
val addedSources: MutableSet<String> = Collections.synchronizedSet(HashSet<String>())
override fun loadState(state: State) {
addedSources.clear()
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")

View File

@@ -13,7 +13,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<version>@snapshot@</version>
<vendor url="http://www.jetbrains.com">JetBrains</vendor>
<idea-version since-build="183.1" until-build="183.*"/>
<idea-version since-build="191.2767" until-build="191.*"/>
<depends>com.intellij.modules.platform</depends>

View File

@@ -0,0 +1,74 @@
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude" version="2" url="http://kotlinlang.org" allow-bundled-update="true">
<id>org.jetbrains.kotlin</id>
<name>Kotlin</name>
<description><![CDATA[
The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<br>
<a href="http://kotlinlang.org/docs/tutorials/getting-started.html">Getting Started in IntelliJ IDEA</a><br>
<a href="http://kotlinlang.org/docs/tutorials/kotlin-android.html">Getting Started in Android Studio</a><br>
<a href="http://slack.kotlinlang.org/">Public Slack</a><br>
<a href="https://youtrack.jetbrains.com/issues/KT">Issue tracker</a><br>
]]></description>
<version>@snapshot@</version>
<vendor url="http://www.jetbrains.com">JetBrains</vendor>
<idea-version since-build="183.1" until-build="183.*"/>
<depends>com.intellij.modules.platform</depends>
<!-- required for Kotlin/Native plugin -->
<depends optional="true">org.jetbrains.kotlin.native.platform.deps</depends>
<depends optional="true" config-file="junit.xml">JUnit</depends>
<depends optional="true" config-file="gradle.xml">org.jetbrains.plugins.gradle</depends>
<depends optional="true" config-file="gradle-java.xml">org.jetbrains.plugins.gradle.java</depends>
<depends optional="true" config-file="maven.xml">org.jetbrains.idea.maven</depends>
<depends optional="true" config-file="testng-j.xml">TestNG-J</depends>
<depends optional="true" config-file="android.xml">org.jetbrains.android</depends>
<depends optional="true" config-file="coverage.xml">Coverage</depends>
<depends optional="true" config-file="i18n.xml">com.intellij.java-i18n</depends>
<depends optional="true" config-file="decompiler.xml">org.jetbrains.java.decompiler</depends>
<depends optional="true" config-file="git4idea.xml">Git4Idea</depends>
<depends optional="true" config-file="stream-debugger.xml">org.jetbrains.debugger.streams</depends>
<!-- ULTIMATE-PLUGIN-PLACEHOLDER -->
<!-- CIDR-PLUGIN-PLACEHOLDER-START -->
<depends>com.intellij.modules.idea</depends>
<depends>com.intellij.modules.java</depends>
<depends optional="true" config-file="javaScriptDebug.xml">JavaScriptDebugger</depends>
<depends optional="true" config-file="kotlin-copyright.xml">com.intellij.copyright</depends>
<depends optional="true" config-file="injection.xml">org.intellij.intelliLang</depends>
<!-- CIDR-PLUGIN-PLACEHOLDER-END -->
<xi:include href="plugin-common.xml" xpointer="xpointer(/idea-plugin/*)"/>
<!-- CIDR-PLUGIN-EXCLUDE-START -->
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
<!-- CIDR-PLUGIN-EXCLUDE-END -->
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="kotlinx-serialization.xml" xpointer="xpointer(/idea-plugin/*)"/>
<extensionPoints>
<xi:include href="extensions/compiler.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.pluginUpdateVerifier"
interface="org.jetbrains.kotlin.idea.update.PluginUpdateVerifier"/>
</extensionPoints>
<xi:include href="plugin-kotlin-extensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
<extensions defaultExtensionNs="com.intellij.jvm">
<declarationSearcher language="kotlin" implementationClass="org.jetbrains.kotlin.idea.jvm.KotlinDeclarationSearcher"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.codeInsight">
<nonBlockingContextChecker implementation="org.jetbrains.kotlin.idea.inspections.blockingCallsDetection.CoroutineNonBlockingContextChecker"/>
</extensions>
</idea-plugin>

View File

@@ -203,8 +203,8 @@ class MoveKotlinDeclarationsProcessor(
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableCollection<UsageInfo>) {
kotlinToLightElements.values.flatten().flatMapTo(result) { lightElement ->
val searchScope = getSearchScope(lightElement) ?: return@flatMapTo emptyList()
val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
val elementName = lightElement.name ?: return@flatMapTo emptyList()
val newFqName = StringUtil.getQualifiedName(newContainerName, elementName)
val foundReferences = HashSet<PsiReference>()
val results = ReferencesSearch

View File

@@ -0,0 +1,373 @@
/*
* 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.ide.highlighter.JavaFileType
import com.intellij.ide.util.EditorHelper
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.refactoring.util.TextOccurrencesUtil
import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewBundle
import com.intellij.usageView.UsageViewDescriptor
import com.intellij.usageView.UsageViewUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.containers.MultiMap
import gnu.trove.THashMap
import gnu.trove.TObjectHashingStrategy
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightElements
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToBeShortenedDescendantsToWaitingSet
import org.jetbrains.kotlin.idea.core.deleteSingle
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.idea.refactoring.broadcastRefactoringExit
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.search.restrictByFileType
import org.jetbrains.kotlin.idea.util.projectStructure.module
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.utils.ifEmpty
import org.jetbrains.kotlin.utils.keysToMap
import java.lang.AssertionError
import java.util.*
interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
object Default : Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration {
return when (targetContainer) {
is KtFile -> {
val declarationContainer: KtElement =
if (targetContainer.isScript()) targetContainer.script!!.blockExpression else targetContainer
declarationContainer.add(originalElement) as KtNamedDeclaration
}
is KtClassOrObject -> targetContainer.addDeclaration(originalElement)
else -> error("Unexpected element: ${targetContainer.getElementTextWithContext()}")
}.apply {
val container = originalElement.containingClassOrObject
if (container is KtObjectDeclaration && container.isCompanion() && container.declarations.singleOrNull() == originalElement) {
container.deleteSingle()
}
else {
originalElement.deleteSingle()
}
}
}
}
}
sealed class MoveSource {
abstract val elementsToMove: Collection<KtNamedDeclaration>
class Elements(override val elementsToMove: Collection<KtNamedDeclaration>) : MoveSource()
class File(val file: KtFile) : MoveSource() {
override val elementsToMove: Collection<KtNamedDeclaration>
get() = file.declarations.filterIsInstance<KtNamedDeclaration>()
}
}
fun MoveSource(declaration: KtNamedDeclaration) = MoveSource.Elements(listOf(declaration))
fun MoveSource(declarations: Collection<KtNamedDeclaration>) = MoveSource.Elements(declarations)
fun MoveSource(file: KtFile) = MoveSource.File(file)
class MoveDeclarationsDescriptor @JvmOverloads constructor(
val project: Project,
val moveSource: MoveSource,
val moveTarget: KotlinMoveTarget,
val delegate: MoveDeclarationsDelegate,
val searchInCommentsAndStrings: Boolean = true,
val searchInNonCode: Boolean = true,
val deleteSourceFiles: Boolean = false,
val moveCallback: MoveCallback? = null,
val openInEditor: Boolean = false,
val allElementsToMove: List<PsiElement>? = null,
val analyzeConflicts: Boolean = true,
val searchReferences: Boolean = true
)
class ConflictUsageInfo(element: PsiElement, val messages: Collection<String>) : UsageInfo(element)
private object ElementHashingStrategy : TObjectHashingStrategy<PsiElement> {
override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean {
if (e1 === e2) return true
// Name should be enough to distinguish different light elements based on the same original declaration
if (e1 is KtLightDeclaration<*, *> && e2 is KtLightDeclaration<*, *>) {
return e1.kotlinOrigin == e2.kotlinOrigin && e1.name == e2.name
}
return e1 == e2
}
override fun computeHashCode(e: PsiElement?): Int {
return when (e) {
null -> 0
is KtLightDeclaration<*, *> -> (e.kotlinOrigin?.hashCode() ?: 0) * 31 + (e.name?.hashCode() ?: 0)
else -> e.hashCode()
}
}
}
class MoveKotlinDeclarationsProcessor(
val descriptor: MoveDeclarationsDescriptor,
val mover: Mover = Mover.Default) : BaseRefactoringProcessor(descriptor.project) {
companion object {
private val REFACTORING_NAME = "Move declarations"
val REFACTORING_ID = "move.kotlin.declarations"
}
val project get() = descriptor.project
private var nonCodeUsages: Array<NonCodeUsageInfo>? = null
private val moveEntireFile = descriptor.moveSource is MoveSource.File
private val elementsToMove = descriptor.moveSource.elementsToMove.filter { e -> e.parent != descriptor.moveTarget.getTargetPsiIfExists(e) }
private val kotlinToLightElementsBySourceFile = elementsToMove
.groupBy { it.containingKtFile }
.mapValues { it.value.keysToMap { it.toLightElements().ifEmpty { listOf(it) } } }
private val conflicts = MultiMap<PsiElement, String>()
override fun getRefactoringId() = REFACTORING_ID
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor {
val targetContainerFqName = descriptor.moveTarget.targetContainerFqName?.let {
if (it.isRoot) UsageViewBundle.message("default.package.presentable.name") else it.asString()
}
return MoveMultipleElementsViewDescriptor(elementsToMove.toTypedArray(), targetContainerFqName)
}
fun getConflictsAsUsages(): List<UsageInfo> = conflicts.entrySet().map { ConflictUsageInfo(it.key, it.value) }
public override fun findUsages(): Array<UsageInfo> {
if (!descriptor.searchReferences || elementsToMove.isEmpty()) return UsageInfo.EMPTY_ARRAY
val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: ""
fun getSearchScope(element: PsiElement): GlobalSearchScope? {
val projectScope = project.projectScope()
val ktDeclaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return projectScope
if (ktDeclaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) return projectScope
val moveTarget = descriptor.moveTarget
val (oldContainer, newContainer) = descriptor.delegate.getContainerChangeInfo(ktDeclaration, moveTarget)
val targetModule = moveTarget.getTargetModule(project) ?: return projectScope
if (oldContainer != newContainer || ktDeclaration.module != targetModule) return projectScope
// Check if facade class may change
if (newContainer is ContainerInfo.Package) {
val javaScope = projectScope.restrictByFileType(JavaFileType.INSTANCE)
val currentFile = ktDeclaration.containingKtFile
val newFile = when (moveTarget) {
is KotlinMoveTargetForExistingElement -> moveTarget.targetElement as? KtFile ?: return null
is KotlinMoveTargetForDeferredFile -> return javaScope
else -> return null
}
val currentFacade = currentFile.findFacadeClass()
val newFacade = newFile.findFacadeClass()
return if (currentFacade?.qualifiedName != newFacade?.qualifiedName) javaScope else null
}
return null
}
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableCollection<UsageInfo>) {
kotlinToLightElements.values.flatten().flatMapTo(result) { lightElement ->
val searchScope = getSearchScope(lightElement) ?: return@flatMapTo emptyList()
val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
val foundReferences = HashSet<PsiReference>()
val results = ReferencesSearch
.search(lightElement, searchScope)
.mapNotNullTo(ArrayList()) { ref ->
if (foundReferences.add(ref) && elementsToMove.none { it.isAncestor(ref.element)}) {
createMoveUsageInfoIfPossible(ref, lightElement, true, false)
}
else null
}
val name = lightElement.getKotlinFqName()?.quoteIfNeeded()?.asString()
if (name != null) {
TextOccurrencesUtil.findNonCodeUsages(
lightElement,
name,
descriptor.searchInCommentsAndStrings,
descriptor.searchInNonCode,
FqName(newFqName).quoteIfNeeded().asString(),
results
)
}
MoveClassHandler.EP_NAME.extensions.forEach { handler ->
if (handler !is MoveKotlinClassHandler) handler.preprocessUsages(results)
}
results
}
}
val usages = ArrayList<UsageInfo>()
val conflictChecker = MoveConflictChecker(
project,
elementsToMove,
descriptor.moveTarget,
elementsToMove.first(),
allElementsToMove = descriptor.allElementsToMove
)
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
val internalUsages = LinkedHashSet<UsageInfo>()
val externalUsages = LinkedHashSet<UsageInfo>()
if (moveEntireFile) {
val changeInfo = ContainerChangeInfo(
ContainerInfo.Package(sourceFile.packageFqName),
descriptor.moveTarget.targetContainerFqName?.let { ContainerInfo.Package(it) } ?: ContainerInfo.UnknownPackage
)
internalUsages += sourceFile.getInternalReferencesToUpdateOnPackageNameChange(changeInfo)
}
else {
kotlinToLightElements.keys.forEach {
val packageNameInfo = descriptor.delegate.getContainerChangeInfo(it, descriptor.moveTarget)
internalUsages += it.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
}
}
internalUsages += descriptor.delegate.findInternalUsages(descriptor)
collectUsages(kotlinToLightElements, externalUsages)
if (descriptor.analyzeConflicts) {
conflictChecker.checkAllConflicts(externalUsages, internalUsages, conflicts)
descriptor.delegate.collectConflicts(descriptor, internalUsages, conflicts)
}
usages += internalUsages
usages += externalUsages
}
return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray())
}
override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean {
return showConflicts(conflicts, refUsages.get())
}
override fun performRefactoring(usages: Array<out UsageInfo>) = doPerformRefactoring(usages.toList())
internal fun doPerformRefactoring(usages: List<UsageInfo>) {
fun moveDeclaration(declaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): KtNamedDeclaration {
val targetContainer = moveTarget.getOrCreateTargetPsi(declaration)
?: throw AssertionError("Couldn't create Kotlin file for: ${declaration::class.java}: ${declaration.text}")
descriptor.delegate.preprocessDeclaration(descriptor, declaration)
if (moveEntireFile) return declaration
return mover(declaration, targetContainer).apply {
addToBeShortenedDescendantsToWaitingSet()
}
}
val (oldInternalUsages, externalUsages) = usages.partition { it is KotlinMoveUsage && it.isInternal }
val newInternalUsages = ArrayList<UsageInfo>()
markInternalUsages(oldInternalUsages)
val usagesToProcess = ArrayList(externalUsages)
try {
descriptor.delegate.preprocessUsages(descriptor, usages)
val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>(ElementHashingStrategy)
val newDeclarations = ArrayList<KtNamedDeclaration>()
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
for ((oldDeclaration, oldLightElements) in kotlinToLightElements) {
val elementListener = transaction?.getElementListener(oldDeclaration)
val newDeclaration = moveDeclaration(oldDeclaration, descriptor.moveTarget)
newDeclarations += newDeclaration
oldToNewElementsMapping[oldDeclaration] = newDeclaration
oldToNewElementsMapping[sourceFile] = newDeclaration.containingKtFile
elementListener?.elementMoved(newDeclaration)
for ((oldElement, newElement) in oldLightElements.asSequence().zip(newDeclaration.toLightElements().asSequence())) {
oldToNewElementsMapping[oldElement] = newElement
}
if (descriptor.openInEditor) {
EditorHelper.openInEditor(newDeclaration)
}
}
if (descriptor.deleteSourceFiles) {
sourceFile.delete()
}
}
val internalUsageScopes: List<KtElement> = if (moveEntireFile) {
newDeclarations.asSequence().map { it.containingKtFile }.distinct().toList()
} else {
newDeclarations
}
internalUsageScopes.forEach { newInternalUsages += restoreInternalUsages(it, oldToNewElementsMapping) }
usagesToProcess += newInternalUsages
nonCodeUsages = postProcessMoveUsages(usagesToProcess, oldToNewElementsMapping).toTypedArray()
}
catch (e: IncorrectOperationException) {
nonCodeUsages = null
RefactoringUIUtil.processIncorrectOperation(myProject, e)
}
finally {
cleanUpInternalUsages(newInternalUsages + oldInternalUsages)
}
}
override fun performPsiSpoilingRefactoring() {
nonCodeUsages?.let { nonCodeUsages -> RenameUtil.renameNonCodeUsages(myProject, nonCodeUsages) }
descriptor.moveCallback?.refactoringCompleted()
}
fun execute(usages: List<UsageInfo>) {
execute(usages.toTypedArray())
}
override fun doRun() {
try {
super.doRun()
} finally {
broadcastRefactoringExit(myProject, refactoringId)
}
}
override fun getCommandName(): String = REFACTORING_NAME
}

View File

@@ -100,7 +100,7 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtil
val calleeExpression = refExpression?.getParentOfTypeAndBranch<KtCallElement> { calleeExpression }
if (calleeExpression != null) {
(ref.resolve() as? KtConstructor<*>)?.let {
return if (flags and JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR != 0) it else it.containingClassOrObject
return if (flags and JavaTargetElementEvaluator().additionalReferenceSearchFlags != 0) it else it.containingClassOrObject
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.search.ideaExtensions
import com.intellij.codeInsight.JavaTargetElementEvaluator
import com.intellij.codeInsight.TargetElementEvaluatorEx
import com.intellij.codeInsight.TargetElementUtil
import com.intellij.codeInsight.TargetElementUtilExtender
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiReference
import com.intellij.util.BitUtil
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.source.getPsi
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx, TargetElementUtilExtender {
companion object {
const val DO_NOT_UNWRAP_LABELED_EXPRESSION = 0x100
const val BYPASS_IMPORT_ALIAS = 0x200
// Place caret after the open curly brace in lambda for generated 'it'
fun findLambdaOpenLBraceForGeneratedIt(ref: PsiReference): PsiElement? {
val element: PsiElement = ref.element
if (element.text != "it") return null
if (element !is KtNameReferenceExpression || !isAutoCreatedItUsage(element)) return null
val itDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() ?: return null
val descriptorWithSource = itDescriptor.containingDeclaration as? DeclarationDescriptorWithSource ?: return null
val lambdaExpression = descriptorWithSource.source.getPsi()?.parent as? KtLambdaExpression ?: return null
return lambdaExpression.leftCurlyBrace.treeNext?.psi
}
// Navigate to receiver element for this in extension declaration
fun findReceiverForThisInExtensionFunction(ref: PsiReference): PsiElement? {
val element: PsiElement = ref.element
if (element.text != "this") return null
if (element !is KtNameReferenceExpression) return null
val callableDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() as? CallableDescriptor ?: return null
if (!callableDescriptor.isExtension) return null
val callableDeclaration = callableDescriptor.source.getPsi() as? KtCallableDeclaration ?: return null
return callableDeclaration.receiverTypeReference
}
}
override fun getAdditionalDefinitionSearchFlags() = 0
override fun getAdditionalReferenceSearchFlags() = DO_NOT_UNWRAP_LABELED_EXPRESSION or BYPASS_IMPORT_ALIAS
override fun getAllAdditionalFlags() = additionalDefinitionSearchFlags + additionalReferenceSearchFlags
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? {
if (ref is KtSimpleNameReference && ref.expression is KtLabelReferenceExpression) {
val refTarget = ref.resolve() as? KtExpression ?: return null
if (!BitUtil.isSet(flags, DO_NOT_UNWRAP_LABELED_EXPRESSION)) {
return refTarget.getLabeledParent(ref.expression.getReferencedName()) ?: refTarget
}
return refTarget
}
if (!BitUtil.isSet(flags, BYPASS_IMPORT_ALIAS)) {
(ref.element as? KtSimpleNameExpression)?.mainReference?.getImportAlias()?.let { return it }
}
// prefer destructing declaration entry to its target if element name is accepted
if (ref is KtDestructuringDeclarationReference && BitUtil.isSet(flags, TargetElementUtil.ELEMENT_NAME_ACCEPTED)) {
return ref.element
}
val refExpression = ref.element as? KtSimpleNameExpression
val calleeExpression = refExpression?.getParentOfTypeAndBranch<KtCallElement> { calleeExpression }
if (calleeExpression != null) {
(ref.resolve() as? KtConstructor<*>)?.let {
return if (flags and JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR != 0) it else it.containingClassOrObject
}
}
if (BitUtil.isSet(flags, TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED)) {
return findLambdaOpenLBraceForGeneratedIt(ref)
?: findReceiverForThisInExtensionFunction(ref)
}
return null
}
override fun isIdentifierPart(file: PsiFile, text: CharSequence?, offset: Int): Boolean {
val elementAtCaret = file.findElementAt(offset)
if (elementAtCaret?.node?.elementType == KtTokens.IDENTIFIER) return true
// '(' is considered identifier part if it belongs to primary constructor without 'constructor' keyword
return elementAtCaret?.getNonStrictParentOfType<KtPrimaryConstructor>()?.textOffset == offset
}
}

View File

@@ -75,9 +75,9 @@ class KotlinSliceProvider : SliceLanguageSupportProvider, SliceUsageTransformer
return listOf(KotlinSliceUsage(usage.element, usage.parent, 0, false))
}
override fun getExpressionAtCaret(atCaret: PsiElement?, dataFlowToThis: Boolean): KtExpression? {
override fun getExpressionAtCaret(atCaret: PsiElement, dataFlowToThis: Boolean): KtExpression? {
val element =
atCaret?.parentsWithSelf
atCaret.parentsWithSelf
?.firstOrNull {
it is KtProperty ||
it is KtParameter ||

View File

@@ -0,0 +1,118 @@
/*
* 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.slicer
import com.intellij.codeInspection.dataFlow.Nullness
import com.intellij.ide.util.treeView.AbstractTreeStructure
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.psi.PsiElement
import com.intellij.slicer.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isPlainWithEscapes
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.isNullabilityFlexible
class KotlinSliceProvider : SliceLanguageSupportProvider, SliceUsageTransformer {
companion object {
val LEAF_ELEMENT_EQUALITY = object : SliceLeafEquality() {
override fun substituteElement(element: PsiElement) = (element as? KtReference)?.resolve() ?: element
}
}
class KotlinGroupByNullnessAction(treeBuilder: SliceTreeBuilder) : GroupByNullnessActionBase(treeBuilder) {
override fun isAvailable() = true
}
val leafAnalyzer by lazy { SliceLeafAnalyzer(LEAF_ELEMENT_EQUALITY, this) }
val nullnessAnalyzer: SliceNullnessAnalyzerBase by lazy {
object : SliceNullnessAnalyzerBase(LEAF_ELEMENT_EQUALITY, this) {
override fun checkNullness(element: PsiElement?): Nullness {
val types = when (element) {
is KtCallableDeclaration -> listOfNotNull((element.resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType)
is KtDeclaration -> emptyList()
is KtExpression -> listOfNotNull(element.analyze(BodyResolveMode.PARTIAL).getType(element))
else -> emptyList()
}
return when {
types.isEmpty() -> return Nullness.UNKNOWN
types.all { KotlinBuiltIns.isNullableNothing(it) } -> Nullness.NULLABLE
types.any { it.isError || TypeUtils.isNullableType(it) || it.isNullabilityFlexible() } -> Nullness.UNKNOWN
else -> Nullness.NOT_NULL
}
}
}
}
override fun createRootUsage(element: PsiElement, params: SliceAnalysisParams) = KotlinSliceUsage(element, params)
override fun transform(usage: SliceUsage): Collection<SliceUsage>? {
if (usage is KotlinSliceUsage) return null
return listOf(KotlinSliceUsage(usage.element, usage.parent, 0, false))
}
override fun getExpressionAtCaret(atCaret: PsiElement?, dataFlowToThis: Boolean): KtExpression? {
val element =
atCaret?.parentsWithSelf
?.firstOrNull {
it is KtProperty ||
it is KtParameter ||
it is KtDeclarationWithBody ||
(it is KtClass && !it.hasExplicitPrimaryConstructor()) ||
(it is KtExpression && it !is KtDeclaration)
}
?.let { KtPsiUtil.safeDeparenthesize(it as KtExpression) } ?: return null
if (dataFlowToThis) {
if (element is KtConstantExpression) return null
if (element is KtStringTemplateExpression && element.isPlainWithEscapes()) return null
if (element is KtClassLiteralExpression) return null
if (element is KtCallableReferenceExpression) return null
}
return element
}
override fun getElementForDescription(element: PsiElement): PsiElement {
return (element as? KtSimpleNameExpression)?.mainReference?.resolve() ?: element
}
override fun getRenderer() = KotlinSliceUsageCellRenderer
override fun startAnalyzeLeafValues(structure: AbstractTreeStructure, finalRunnable: Runnable) {
leafAnalyzer.startAnalyzeValues(structure, finalRunnable)
}
override fun startAnalyzeNullness(structure: AbstractTreeStructure, finalRunnable: Runnable) {
nullnessAnalyzer.startAnalyzeNullness(structure, finalRunnable)
}
override fun registerExtraPanelActions(group: DefaultActionGroup, builder: SliceTreeBuilder) {
if (builder.dataFlowToThis) {
group.add(GroupByLeavesAction(builder))
group.add(KotlinGroupByNullnessAction(builder))
}
}
}

View File

@@ -122,6 +122,12 @@ open class KotlinUMethod(
override fun getOriginalElement(): PsiElement? = super<UAnnotationMethod>.getOriginalElement()
override val returnTypeReference: UTypeReferenceExpression? by lz {
(sourcePsi as? KtCallableDeclaration)?.typeReference?.let {
LazyKotlinUTypeReferenceExpression(it, this) { javaPsi.returnType ?: UastErrorType }
}
}
override fun equals(other: Any?) = other is KotlinUMethod && psi == other.psi
companion object {

View File

@@ -0,0 +1,138 @@
/*
* 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.*
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, UMethodTypeSpecific, UAnchorOwner, JavaUElementWithComments, PsiMethod by psi {
override val comments: List<UComment>
get() = super<KotlinAbstractUElement>.comments
override val psi: KtLightMethod = unwrap<UMethod, KtLightMethod>(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<UParameter>()
val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, this))
lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, this) }
uParameters
}
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 {
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<UAnnotationMethod>.getBody()
override fun getOriginalElement(): PsiElement? = super<UAnnotationMethod>.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)
}
}

View File

@@ -9,6 +9,7 @@ import org.jetbrains.uast.evaluation.UEvaluationState
import org.jetbrains.uast.kotlin.KotlinBinaryOperators
import org.jetbrains.uast.kotlin.KotlinPostfixOperators
import org.jetbrains.uast.values.*
import org.jetbrains.uast.evaluation.to
class KotlinEvaluatorExtension : AbstractEvaluatorExtension(KotlinLanguage.INSTANCE) {

View File

@@ -0,0 +1,56 @@
package org.jetbrains.uast.kotlin.evaluation
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.uast.UBinaryExpression
import org.jetbrains.uast.UastPostfixOperator
import org.jetbrains.uast.evaluation.AbstractEvaluatorExtension
import org.jetbrains.uast.evaluation.UEvaluationInfo
import org.jetbrains.uast.evaluation.UEvaluationState
import org.jetbrains.uast.kotlin.KotlinBinaryOperators
import org.jetbrains.uast.kotlin.KotlinPostfixOperators
import org.jetbrains.uast.values.*
class KotlinEvaluatorExtension : AbstractEvaluatorExtension(KotlinLanguage.INSTANCE) {
private data class Range(val from: UValue, val to: UValue) {
override fun toString() = "$from..$to"
}
private class UClosedRangeConstant(override val value: Range, override val source: UBinaryExpression?) : UAbstractConstant() {
constructor(from: UValue, to: UValue, source: UBinaryExpression): this(Range(from, to), source)
}
override fun evaluatePostfix(
operator: UastPostfixOperator,
operandValue: UValue,
state: UEvaluationState
): UEvaluationInfo {
return when (operator) {
KotlinPostfixOperators.EXCLEXCL -> when (operandValue.toConstant()) {
UNullConstant -> UValue.UNREACHABLE
is UConstant -> operandValue
else -> UUndeterminedValue
} to state
else -> return super.evaluatePostfix(operator, operandValue, state)
}
}
private fun UValue.contains(value: UValue): UValue {
val range = (this as? UClosedRangeConstant)?.value ?: return UUndeterminedValue
return (value greaterOrEquals range.from) and (value lessOrEquals range.to)
}
override fun evaluateBinary(
binaryExpression: UBinaryExpression,
leftValue: UValue,
rightValue: UValue,
state: UEvaluationState
): UEvaluationInfo {
return when (binaryExpression.operator) {
KotlinBinaryOperators.IN -> rightValue.contains(leftValue)
KotlinBinaryOperators.NOT_IN -> !rightValue.contains(leftValue)
KotlinBinaryOperators.RANGE_TO -> UClosedRangeConstant(leftValue, rightValue, binaryExpression)
else -> UUndeterminedValue
} to state
}
}

View File

@@ -456,6 +456,22 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
}
}
@Test
fun testMethodReturnTypeReference() {
doTest("Elvis") { _, file ->
assertEquals(
"UTypeReferenceExpression (name = java.lang.String)",
file.findElementByTextFromPsi<UMethod>("fun foo(bar: String): String? = null").returnTypeReference?.asLogString()
)
assertEquals(
null,
file.findElementByTextFromPsi<UMethod>("fun bar() = 42").returnTypeReference?.asLogString()
)
}
}
}
fun <T, R> Iterable<T>.assertedFind(value: R, transform: (T) -> R): T =

View File

@@ -0,0 +1,462 @@
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.test.testFramework.KtUsefulTestCase
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin
import org.jetbrains.uast.test.env.kotlin.findElementByText
import org.jetbrains.uast.test.env.kotlin.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<UAnnotation>("@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<KtAnnotationEntry>().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<ULiteralExpression>("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<KtStringTemplateExpression>(false)
val uLiteral = stringTemplate.toUElementOfType<ULiteralExpression>()
assertNull(uLiteral)
}
}
@Test fun testNameContainingFile() {
doTest("NameContainingFile") { _, file ->
val foo = file.findElementByText<UClass>("class Foo")
assertEquals(file.psi, foo.nameIdentifier!!.containingFile)
val bar = file.findElementByText<UMethod>("fun bar() {}")
assertEquals(file.psi, bar.nameIdentifier!!.containingFile)
val xyzzy = file.findElementByText<UVariable>("val xyzzy: Int = 0")
assertEquals(file.psi, xyzzy.nameIdentifier!!.containingFile)
}
}
@Test fun testInterfaceMethodWithBody() {
doTest("DefaultImpls") { _, file ->
val bar = file.findElementByText<UMethod>("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<ULambdaExpression>("{ /* Not SAM */ }").functionalInterfaceType)
assertEquals("java.lang.Runnable",
file.findElementByText<ULambdaExpression>("{/* Variable */}").functionalInterfaceType?.canonicalText)
assertEquals("java.lang.Runnable",
file.findElementByText<ULambdaExpression>("{/* Assignment */}").functionalInterfaceType?.canonicalText)
assertEquals("java.lang.Runnable",
file.findElementByText<ULambdaExpression>("{/* Type Cast */}").functionalInterfaceType?.canonicalText)
assertEquals("java.lang.Runnable",
file.findElementByText<ULambdaExpression>("{/* Argument */}").functionalInterfaceType?.canonicalText)
assertEquals("java.lang.Runnable",
file.findElementByText<ULambdaExpression>("{/* Return */}").functionalInterfaceType?.canonicalText)
assertEquals(
"java.lang.Runnable",
file.findElementByText<ULambdaExpression>("{ /* SAM */ }").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<KtUserType>(false)!!
assertNotNull(element.getUastParentOfType(UAnnotation::class.java))
}
}
@Test fun testElvisType() {
doTest("ElvisType") { _, file ->
val elvisExpression = file.findElementByText<UExpression>("text ?: return")
assertEquals("String", elvisExpression.getExpressionType()!!.presentableText)
}
}
@Test fun testFindAttributeDefaultValue() {
doTest("AnnotationParameters") { _, file ->
val witDefaultValue = file.findElementByText<UAnnotation>("@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<KtBinaryExpression>(false)!!
val uBinaryExpression = KotlinUastLanguagePlugin().convertElementWithParent(binaryExpression, null)!!
UsefulTestCase.assertInstanceOf(uBinaryExpression.uastParent, UIfExpression::class.java)
}
}
@Test
fun testWhenStringLiteral() {
doTest("WhenStringLiteral") { _, file ->
file.findElementByTextFromPsi<ULiteralExpression>("abc").let { literalExpression ->
val psi = literalExpression.psi!!
Assert.assertTrue(psi is KtLiteralStringTemplateEntry)
UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java)
}
file.findElementByTextFromPsi<ULiteralExpression>("def").let { literalExpression ->
val psi = literalExpression.psi!!
Assert.assertTrue(psi is KtLiteralStringTemplateEntry)
UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java)
}
file.findElementByTextFromPsi<ULiteralExpression>("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<UExpression>("val (bindingContext, statementFilter) = arr").let { e ->
val uBlockExpression = e.getParentOfType<UBlockExpression>()
Assert.assertNotNull(uBlockExpression)
val uMethod = uBlockExpression!!.getParentOfType<UMethod>()
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<UField>("@kotlin.SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field ->
val annotation = field.annotations.assertedFind("kotlin.SinceKotlin") { it.qualifiedName }
Assert.assertEquals("1.0", annotation.findDeclaredAttributeValue("version")?.evaluateString())
Assert.assertEquals("SinceKotlin", annotation.cast<UAnchorOwner>().uastAnchor?.sourcePsi?.text)
}
}
}
fun UFile.checkUastSuperTypes(refText: String, superTypes: List<String>) {
findElementByTextFromPsi<UClass>(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<UCallExpression>("intArrayOf(1, 2, 3)").let { field ->
Assert.assertEquals("PsiType:int[]", field.returnType.toString())
}
file.findElementByTextFromPsi<UCallExpression>("[1, 2, 3]").let { field ->
Assert.assertEquals("PsiType:int[]", field.returnType.toString())
Assert.assertEquals("PsiType:int", field.typeArguments.single().toString())
}
file.findElementByTextFromPsi<UCallExpression>("[\"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<UElement>("@AnnotationArray(value = Annotation())")
.findElementByTextFromPsi<UElement>("Annotation()")
.sourcePsiElement
.let { referenceExpression ->
val convertedUAnnotation = referenceExpression
.cast<KtReferenceExpression>()
.toUElementOfType<UAnnotation>()
?: throw AssertionError("haven't got annotation from $referenceExpression(${referenceExpression?.javaClass})")
checkDescriptorsLeak(convertedUAnnotation)
assertEquals("Annotation", convertedUAnnotation.qualifiedName)
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<String?>?, refText: String) =
file.findElementByTextFromPsi<UCallExpression>(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")
}
@Test
fun testResolvedDeserializedMethod() = doTest("Resolve") { _, file ->
val barMethod = file.findElementByTextFromPsi<UElement>("bar").getParentOfType<UMethod>()!!
fun UElement.assertResolveCall(callText: String, methodName: String = callText.substringBefore("(")) {
this.findElementByTextFromPsi<UCallExpression>(callText).let {
val resolve = it.resolve().sure { "resolving '$callText'" }
assertEquals(methodName, resolve.name)
}
}
barMethod.assertResolveCall("foo()")
barMethod.assertResolveCall("inlineFoo()")
barMethod.assertResolveCall("forEach { println(it) }", "forEach")
barMethod.assertResolveCall("joinToString()")
barMethod.assertResolveCall("last()")
barMethod.assertResolveCall("setValue(\"123\")")
barMethod.assertResolveCall("contains(2 as Int)", "longRangeContains")
barMethod.assertResolveCall("IntRange(1, 2)")
}
@Test
fun testUtilsStreamLambda() {
doTest("Lambdas") { _, file ->
val lambda = file.findElementByTextFromPsi<ULambdaExpression>("{ it.isEmpty() }")
assertEquals(
"java.util.function.Predicate<? super java.lang.String>",
lambda.functionalInterfaceType?.canonicalText
)
assertEquals(
"kotlin.jvm.functions.Function1<? super java.lang.String,? extends java.lang.Boolean>",
lambda.getExpressionType()?.canonicalText
)
val uCallExpression = lambda.uastParent.assertedCast<UCallExpression> { "UCallExpression expected" }
assertTrue(uCallExpression.valueArguments.contains(lambda))
}
}
@Test
fun testLambdaParamCall() {
doTest("Lambdas") { _, file ->
val lambdaCall = file.findElementByTextFromPsi<UCallExpression>("selectItemFunction()")
assertEquals(
"UIdentifier (Identifier (selectItemFunction))",
lambdaCall.methodIdentifier?.asLogString()
)
assertEquals(
"selectItemFunction",
lambdaCall.methodIdentifier?.name
)
assertEquals(
"invoke",
lambdaCall.methodName
)
val receiver = lambdaCall.receiver ?: kotlin.test.fail("receiver expected")
assertEquals("UReferenceExpression", receiver.asLogString())
val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected")
assertEquals("UParameter (name = selectItemFunction)", uParameter.asLogString())
}
}
@Test
fun testLocalLambdaCall() {
doTest("Lambdas") { _, file ->
val lambdaCall = file.findElementByTextFromPsi<UCallExpression>("baz()")
assertEquals(
"UIdentifier (Identifier (baz))",
lambdaCall.methodIdentifier?.asLogString()
)
assertEquals(
"baz",
lambdaCall.methodIdentifier?.name
)
assertEquals(
"invoke",
lambdaCall.methodName
)
val receiver = lambdaCall.receiver ?: kotlin.test.fail("receiver expected")
assertEquals("UReferenceExpression", receiver.asLogString())
val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected")
assertEquals("ULocalVariable (name = baz)", uParameter.asLogString())
}
}
@Test
fun testLocalDeclarationCall() {
doTest("LocalDeclarations") { _, file ->
val localFunction = file.findElementByTextFromPsi<UElement>("bar() == Local()").
findElementByText<UCallExpression>("bar()")
assertEquals(
"UIdentifier (Identifier (bar))",
localFunction.methodIdentifier?.asLogString()
)
assertEquals(
"bar",
localFunction.methodIdentifier?.name
)
assertEquals(
"bar",
localFunction.methodName
)
assertNull(localFunction.resolve())
val receiver = localFunction.receiver ?: kotlin.test.fail("receiver expected")
assertEquals("UReferenceExpression", receiver.asLogString())
val uParameter = (receiver as UReferenceExpression).resolve().toUElement() ?: kotlin.test.fail("uelement expected")
assertEquals("ULambdaExpression", uParameter.asLogString())
}
}
}
fun <T, R> Iterable<T>.assertedFind(value: R, transform: (T) -> R): T =
find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}")

View File

@@ -5,21 +5,10 @@
package org.jetbrains.kotlin.statistics
import com.intellij.ide.plugins.PluginManager
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsageTriggerCollector
import com.intellij.internal.statistic.service.fus.collectors.FUSApplicationUsageTrigger
import com.intellij.internal.statistic.service.fus.collectors.FUSUsageContext
import com.intellij.openapi.extensions.PluginId
open class KotlinStatisticsTrigger(private val groupIdSufix: String) : ApplicationUsageTriggerCollector() {
override fun getGroupId() = "statistics.kotlin.$groupIdSufix"
open class KotlinStatisticsTrigger(private val groupIdSufix: String) {
companion object {
public fun trigger(clazz: Class<out KotlinStatisticsTrigger>, event: String) {
val plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin"))
val version = plugin?.version ?: "undefined"
FUSApplicationUsageTrigger.getInstance().trigger(clazz, event, FUSUsageContext.create(version))
// FUS is not working for 182
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.statistics
import com.intellij.ide.plugins.PluginManager
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsageTriggerCollector
import com.intellij.internal.statistic.service.fus.collectors.FUSApplicationUsageTrigger
import com.intellij.internal.statistic.service.fus.collectors.FUSUsageContext
import com.intellij.openapi.extensions.PluginId
open class KotlinStatisticsTrigger(private val groupIdSufix: String) : ApplicationUsageTriggerCollector() {
override fun getGroupId() = "statistics.kotlin.$groupIdSufix"
companion object {
public fun trigger(clazz: Class<out KotlinStatisticsTrigger>, event: String) {
val plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin"))
val version = plugin?.version ?: "undefined"
FUSApplicationUsageTrigger.getInstance().trigger(clazz, event, FUSUsageContext.create(version))
}
}
}
open class KotlinIdeStatisticsTrigger(groupIdSufix: String) : KotlinStatisticsTrigger("ide.$groupIdSufix")
open class KotlinGradlePluginStatisticsTrigger(groupIdSufix: String) : KotlinStatisticsTrigger("gradle.$groupIdSufix")
open class KotlinMavenPluginStatisticsTrigger(groupIdSufix: String) : KotlinStatisticsTrigger("maven.$groupIdSufix")
open class KotlinJPSPluginStatisticsTrigger(groupIdSufix: String) : KotlinStatisticsTrigger("jps.$groupIdSufix")
class KotlinVersionTrigger : KotlinGradlePluginStatisticsTrigger("kotlin_version")
class KotlinTargetTrigger : KotlinGradlePluginStatisticsTrigger("target")
class KotlinMavenTargetTrigger : KotlinMavenPluginStatisticsTrigger("target")
class KotlinJPSTargetTrigger : KotlinJPSPluginStatisticsTrigger("target")
class KotlinProjectLibraryUsageTrigger : KotlinGradlePluginStatisticsTrigger("library")
open class KotlinIdeActionTrigger(groupIdSufix: String? = null) : KotlinIdeStatisticsTrigger("action" + (if (groupIdSufix != null) ".$groupIdSufix" else ""))
class KotlinIdeRefactoringTrigger : KotlinIdeActionTrigger("refactoring")
class KotlinIdeNewFileTemplateTrigger : KotlinIdeStatisticsTrigger("newFileTempl")