~~~~ switch 191 ~~~~
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt index a6d2a75..ebb3b44 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt
@@ -51,7 +51,11 @@ 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() }
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt.183 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt.183 new file mode 100644 index 0000000..a6d2a75 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/MockExternalAnnotationsManager.kt.183
@@ -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") + } +}
diff --git a/compiler/compiler.pro b/compiler/compiler.pro index 10a361d..01c7dce 100644 --- a/compiler/compiler.pro +++ b/compiler/compiler.pro
@@ -16,6 +16,7 @@ -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 @@ -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 { *; }
diff --git a/compiler/compiler.pro.183 b/compiler/compiler.pro.183 new file mode 100644 index 0000000..10a361d --- /dev/null +++ b/compiler/compiler.pro.183
@@ -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 { *; }
diff --git a/gradle/versions.properties b/gradle/versions.properties index fd0f144..12bd112 100644 --- a/gradle/versions.properties +++ b/gradle/versions.properties
@@ -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.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
diff --git a/gradle/versions.properties.183 b/gradle/versions.properties.183 new file mode 100644 index 0000000..fd0f144 --- /dev/null +++ b/gradle/versions.properties.183
@@ -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
diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt index b9172990..09884bf 100644 --- a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt
@@ -11,17 +11,14 @@ 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) } \ No newline at end of file
diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt.183 b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt.183 new file mode 100644 index 0000000..b9172990 --- /dev/null +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinResourceRootType.kt.183
@@ -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) +} \ No newline at end of file
diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinRootType.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinRootType.kt.183 similarity index 100% rename from idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinRootType.kt rename to idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinRootType.kt.183
diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt index 8b5b3d9..551c8d2 100644 --- a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt
@@ -11,20 +11,17 @@ 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) }
diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt.183 b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt.183 new file mode 100644 index 0000000..8b5b3d9 --- /dev/null +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinSourceRootType.kt.183
@@ -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) +} +
diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt index e60bc8b..c4f6f68 100644 --- a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt
@@ -25,10 +25,10 @@ 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 @@ if (toBeDownloaded.isNotEmpty()) { MavenProjectsManager.getInstance(module.project) - .scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncResult()) + .scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncPromise()) } }
diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt.183 b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt.183 new file mode 100644 index 0000000..e60bc8b --- /dev/null +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt.183
@@ -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")
diff --git a/idea/resources/META-INF/plugin.xml b/idea/resources/META-INF/plugin.xml index 905aec9..79d4368 100644 --- a/idea/resources/META-INF/plugin.xml +++ b/idea/resources/META-INF/plugin.xml
@@ -13,7 +13,7 @@ <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>
diff --git a/idea/resources/META-INF/plugin.xml.183 b/idea/resources/META-INF/plugin.xml.183 new file mode 100644 index 0000000..905aec9 --- /dev/null +++ b/idea/resources/META-INF/plugin.xml.183
@@ -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>
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt index 8786660..00ea1bd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt
@@ -203,8 +203,8 @@ 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
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt.183 b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt.183 new file mode 100644 index 0000000..8786660 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt.183
@@ -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 +} \ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt index 2139192..00e2f45 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt
@@ -100,7 +100,7 @@ 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 } }
diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt.183 b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt.183 new file mode 100644 index 0000000..2139192 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt.183
@@ -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 + } +}
diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt index 3e72d6c..c1e6d7e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt
@@ -75,9 +75,9 @@ 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 ||
diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt.183 b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt.183 new file mode 100644 index 0000000..3e72d6c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt.183
@@ -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)) + } + } +} \ No newline at end of file
diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt index 908675a..825b11e 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt
@@ -122,6 +122,12 @@ 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 {
diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.183 new file mode 100644 index 0000000..908675a --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt.183
@@ -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) + } +} \ No newline at end of file
diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt index e2642d6..36ad440 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt
@@ -9,6 +9,7 @@ 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) {
diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt.183 new file mode 100644 index 0000000..e2642d6 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/evaluation/KotlinEvaluatorExtension.kt.183
@@ -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 + } +} \ No newline at end of file
diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt index 42c2928..798d453 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt
@@ -456,6 +456,22 @@ } } + @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 =
diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.183 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.183 new file mode 100644 index 0000000..42c2928 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.183
@@ -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() }}")
diff --git a/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt b/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt index a5556c3..e5183a7 100644 --- a/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt +++ b/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt
@@ -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 } } }
diff --git a/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt.183 b/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt.183 new file mode 100644 index 0000000..a5556c3 --- /dev/null +++ b/usage-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/ProjectScopeStatsReporters.kt.183
@@ -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") \ No newline at end of file