| /* |
| * Copyright 2010-2017 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.jetbrains.uast.test.kotlin |
| |
| import com.intellij.psi.PsiElement |
| import com.intellij.psi.PsiFile |
| import com.intellij.psi.PsiLanguageInjectionHost |
| import com.intellij.refactoring.rename.RenameProcessor |
| import com.intellij.refactoring.rename.RenamePsiElementProcessor |
| import com.intellij.testFramework.LightProjectDescriptor |
| import com.intellij.testFramework.UsefulTestCase |
| import junit.framework.TestCase |
| import org.jetbrains.kotlin.asJava.elements.KtLightElement |
| import org.jetbrains.kotlin.idea.core.copied |
| import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase |
| import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor |
| import org.jetbrains.kotlin.psi.KtCallExpression |
| import org.jetbrains.kotlin.psi.KtClass |
| import org.jetbrains.kotlin.psi.KtFile |
| import org.jetbrains.kotlin.psi.KtPsiFactory |
| import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType |
| import org.jetbrains.kotlin.psi.psiUtil.findFunctionByName |
| import org.jetbrains.kotlin.psi.psiUtil.getParentOfType |
| import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner |
| import org.jetbrains.kotlin.test.testFramework.runWriteAction |
| import org.jetbrains.uast.* |
| import org.jetbrains.uast.test.env.kotlin.findUElementByTextFromPsi |
| import org.junit.runner.RunWith |
| |
| @RunWith(JUnit3WithIdeaConfigurationRunner::class) |
| class KotlinDetachedUastTest : KotlinLightCodeInsightFixtureTestCase() { |
| |
| override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE |
| |
| fun testLiteralInAnnotation() { |
| |
| val psiFile = myFixture.configureByText("AnnotatedClass.kt", """ |
| class AnnotatedClass { |
| @JvmName(name = "") |
| fun bar(param: String) = null |
| } |
| """.trimIndent()) |
| |
| fun psiElement(file: PsiFile): PsiElement = file.findElementAt(file.text.indexOf("\"\"")).orFail("literal") |
| .getParentOfType<PsiLanguageInjectionHost>(false).orFail("host") |
| .toUElement().orFail("uElement").getParentOfType<UClass>(false) |
| .orFail("UClass").psi.orFail("psi") |
| |
| psiElement(psiFile).let { |
| // Otherwise following asserts have no sense |
| TestCase.assertTrue("psi element should be light ", it is KtLightElement<*, *>) |
| } |
| val copied = psiFile.copied() |
| TestCase.assertNull("virtual file for copy should be null", copied.virtualFile) |
| TestCase.assertNotNull("psi element in copy", psiElement(copied)) |
| TestCase.assertSame("copy.originalFile should be psiFile", copied.originalFile, psiFile) |
| TestCase.assertSame("virtualFiles of element and file itself should be the same", |
| psiElement(copied).containingFile.originalFile.virtualFile, |
| copied.originalFile.virtualFile) |
| } |
| |
| fun testDetachedResolve() { |
| val psiFile = myFixture.configureByText( |
| "AnnotatedClass.kt", """ |
| class AnnotatedClass { |
| @JvmName(name = "") |
| fun bar(param: String) { unknownFunc(param) } |
| } |
| """.trimIndent() |
| ) as KtFile |
| |
| val detachedCall = psiFile.findDescendantOfType<KtCallExpression>()!!.copied() |
| val uCallExpression = detachedCall.toUElementOfType<UCallExpression>()!! |
| // at least it should not throw exceptions |
| TestCase.assertNull(uCallExpression.methodName) |
| } |
| |
| fun testCapturedTypeInExtensionReceiverOfCall() { |
| val psiFile = myFixture.configureByText( |
| "foo.kt", """ |
| class Foo<T> |
| |
| fun <K> K.extensionFunc() {} |
| |
| fun test(f: Foo<*>) { |
| f.extensionFunc() |
| } |
| """.trimIndent() |
| ) as KtFile |
| |
| val extensionFunctionCall = psiFile.findDescendantOfType<KtCallExpression>()!! |
| val uCallExpression = extensionFunctionCall.toUElementOfType<UCallExpression>()!! |
| |
| TestCase.assertNotNull(uCallExpression.receiverType) |
| TestCase.assertNotNull(uCallExpression.methodName) |
| } |
| |
| fun testParameterInAnnotationClassFromFactory() { |
| |
| val detachedClass = KtPsiFactory(project).createClass(""" |
| annotation class MyAnnotation(val myParam: String = "default") |
| """) |
| |
| detachedClass.findUElementByTextFromPsi<UElement>("default") |
| .getParentOfType<UExpression>().let { |
| TestCase.assertNotNull("it should return something at least", it) |
| } |
| |
| } |
| |
| fun testLiteralInClassInitializerFromFactory() { |
| |
| val detachedClass = KtPsiFactory(project).createClass(""" |
| class MyAnnotation(){ |
| init { |
| "default" |
| } |
| } |
| """) |
| |
| val literalInside = detachedClass.findUElementByTextFromPsi<UElement>("default") |
| generateSequence(literalInside, { it.uastParent }).count().let { |
| TestCase.assertTrue("it should have some parents $it actually", it > 1) |
| } |
| |
| } |
| |
| fun testAnonymousInnerClassWithIDELightClasses() { |
| |
| val detachedClass = myFixture.configureByText( |
| "MyClass.kt", """ |
| class MyClass() { |
| private val obj = object : MyClass() {} |
| } |
| """ |
| ) |
| |
| val anonymousClass = detachedClass.findUElementByTextFromPsi<UObjectLiteralExpression>("object : MyClass() {}") |
| .let { uObjectLiteralExpression -> uObjectLiteralExpression.declaration } |
| TestCase.assertEquals( |
| "UClass (name = null), UObjectLiteralExpression, UField (name = obj), UClass (name = MyClass), UFile (package = )", |
| generateSequence<UElement>(anonymousClass, { it.uastParent }).joinToString { it.asLogString() }) |
| |
| } |
| |
| |
| fun testDontConvertDetachedFunctions() { |
| val ktFile = myFixture.configureByText( |
| "MyClass.kt", """ |
| class MyClass() { |
| fun foo1() = 42 |
| } |
| """ |
| ) as KtFile |
| val ktClass = ktFile.declarations.filterIsInstance<KtClass>().single()//.copied() |
| val ktFunctionDetached = ktClass.findFunctionByName("foo1")!! |
| runWriteAction { ktClass.delete() } |
| TestCase.assertNull(ktFunctionDetached.toUElementOfType<UMethod>()) |
| } |
| |
| fun testRenameHandlers() { |
| myFixture.configureByText( |
| "JavaClass.java", """ |
| class JavaClass { |
| void foo(){ |
| new MyClass().getBar(); |
| } |
| } |
| """ |
| ) |
| |
| myFixture.configureByText( |
| "MyClass.kt", """ |
| class MyClass() { |
| val b<caret>ar = 42 |
| } |
| """ |
| ) |
| |
| val element = myFixture.elementAtCaret |
| |
| val substitution = |
| RenamePsiElementProcessor.forElement(element).substituteElementToRename(element, editor).orFail("no element") |
| val linkedMapOf = linkedMapOf<PsiElement, String>() |
| RenameProcessor(project, substitution, "newName", false, false) |
| .prepareRenaming(element, "newName", linkedMapOf) |
| |
| UsefulTestCase.assertTrue(linkedMapOf.any()) |
| |
| for ((k, _) in linkedMapOf) { |
| TestCase.assertEquals(element, k.toUElement()?.sourcePsi) |
| } |
| } |
| |
| } |
| |
| |
| fun <T> T?.orFail(msg: String): T = this ?: error(msg) |