From 498f24b98be2fd5f0b76e126677dd82e66ff757e Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 9 Apr 2015 15:21:56 +0300 Subject: [PATCH] KT-7291 Copy/paste of java declaration with preceding comment omitted does not convert it to Kotlin propertly #KT-7291 Fixed --- .../kotlin/psi/psiUtil/jetPsiUtil.kt | 18 +- .../idea/conversion/copy/DataForConversion.kt | 182 ++++++++++++++++-- .../kotlin/idea/conversion/copy/RangeUtils.kt | 3 + .../ClassWithNoDocComment.expected.kt | 6 + .../conversion/ClassWithNoDocComment.java | 10 + .../FieldWithNoEndComment.expected.kt | 3 + .../conversion/FieldWithNoEndComment.java | 3 + ...ldWithNoModifierAndNoSemicolon.expected.kt | 3 + .../FieldWithNoModifierAndNoSemicolon.java | 3 + .../FileWithNoPackageStatement.expected.kt | 9 + .../FileWithNoPackageStatement.java | 10 + .../MethodDeclarationWithNoBody.expected.kt | 3 + .../MethodDeclarationWithNoBody.java | 5 + .../MethodWithNoAnnotation.expected.kt | 5 + .../conversion/MethodWithNoAnnotation.java | 6 + .../MethodWithOnlyOneAnnotation.expected.kt | 6 + .../MethodWithOnlyOneAnnotation.java | 7 + ...otlinCopyPasteConversionTestGenerated.java | 43 ++++- 18 files changed, 304 insertions(+), 21 deletions(-) create mode 100644 idea/testData/copyPaste/conversion/ClassWithNoDocComment.expected.kt create mode 100644 idea/testData/copyPaste/conversion/ClassWithNoDocComment.java create mode 100644 idea/testData/copyPaste/conversion/FieldWithNoEndComment.expected.kt create mode 100644 idea/testData/copyPaste/conversion/FieldWithNoEndComment.java create mode 100644 idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.expected.kt create mode 100644 idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.java create mode 100644 idea/testData/copyPaste/conversion/FileWithNoPackageStatement.expected.kt create mode 100644 idea/testData/copyPaste/conversion/FileWithNoPackageStatement.java create mode 100644 idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.expected.kt create mode 100644 idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.java create mode 100644 idea/testData/copyPaste/conversion/MethodWithNoAnnotation.expected.kt create mode 100644 idea/testData/copyPaste/conversion/MethodWithNoAnnotation.java create mode 100644 idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.expected.kt create mode 100644 idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.java diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 9203a72fd85..80568f2ba18 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -367,16 +367,16 @@ public fun JetExpression.isFunctionLiteralOutsideParentheses(): Boolean { } } -public fun PsiElement.siblings(forward: Boolean = true, withItself: Boolean = true): Stream { +public fun PsiElement.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence { val stepFun = if (forward) { e: PsiElement -> e.getNextSibling() } else { e: PsiElement -> e.getPrevSibling() } - val stream = stream(this, stepFun) - return if (withItself) stream else stream.drop(1) + val sequence = sequence(this, stepFun) + return if (withItself) sequence else sequence.drop(1) } -public fun ASTNode.siblings(forward: Boolean = true, withItself: Boolean = true): Stream { +public fun ASTNode.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence { val stepFun = if (forward) { node: ASTNode -> node.getTreeNext() } else { e: ASTNode -> e.getTreeNext() } - val stream = stream(this, stepFun) - return if (withItself) stream else stream.drop(1) + val sequence = sequence(this, stepFun) + return if (withItself) sequence else sequence.drop(1) } public fun PsiElement.parents(withItself: Boolean = true): Sequence { @@ -384,9 +384,9 @@ public fun PsiElement.parents(withItself: Boolean = true): Sequence return if (withItself) sequence else sequence.drop(1) } -public fun ASTNode.parents(withItself: Boolean = true): Stream { - val stream = stream(this) { it.getTreeParent() } - return if (withItself) stream else stream.drop(1) +public fun ASTNode.parents(withItself: Boolean = true): Sequence { + val sequence = sequence(this) { it.getTreeParent() } + return if (withItself) sequence else sequence.drop(1) } public fun JetExpression.getAssignmentByLHS(): JetBinaryExpression? { diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt index 23d328a631b..29807bf084a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/DataForConversion.kt @@ -19,11 +19,12 @@ package org.jetbrains.kotlin.idea.conversion.copy import com.intellij.lang.java.JavaLanguage import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiFileFactory -import com.intellij.psi.PsiJavaFile +import com.intellij.psi.* import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.psiUtil.elementsInRange +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.psi.psiUtil.siblings import java.util.ArrayList data class DataForConversion private( @@ -32,20 +33,179 @@ data class DataForConversion private( ) { companion object { fun prepare(copiedCode: CopiedJavaCode, project: Project): DataForConversion { - assert(copiedCode.startOffsets.size() == copiedCode.endOffsets.size(), "Must have the same size") + val startOffsets = copiedCode.startOffsets.clone() + val endOffsets = copiedCode.endOffsets.clone() + assert(startOffsets.size() == endOffsets.size(), "Must have the same size") - val sourceFileText = copiedCode.fileText - val sourceFile = PsiFileFactory.getInstance(project).createFileFromText(JavaLanguage.INSTANCE, sourceFileText) as PsiJavaFile - val importsAndPackage = buildImportsAndPackage(sourceFile) + var fileText = copiedCode.fileText + var file = PsiFileFactory.getInstance(project).createFileFromText(JavaLanguage.INSTANCE, fileText) as PsiJavaFile + + val importsAndPackage = buildImportsAndPackage(file) + + val newFileText = clipTextIfNeeded(file, fileText, startOffsets, endOffsets) + if (newFileText != null) { + fileText = newFileText + file = PsiFileFactory.getInstance(project).createFileFromText(JavaLanguage.INSTANCE, newFileText) as PsiJavaFile + } val elementsAndTexts = ArrayList() - for (i in copiedCode.startOffsets.indices) { - elementsAndTexts.collectElementsToConvert(sourceFile, sourceFileText, TextRange(copiedCode.startOffsets[i], copiedCode.endOffsets[i])) + for (i in startOffsets.indices) { + elementsAndTexts.collectElementsToConvert(file, fileText, TextRange(startOffsets[i], endOffsets[i])) } return DataForConversion(elementsAndTexts, importsAndPackage) } + private fun clipTextIfNeeded(file: PsiJavaFile, fileText: String, startOffsets: IntArray, endOffsets: IntArray): String? { + fun canDropRange(range: TextRange) + = startOffsets.indices.all { range !in TextRange(startOffsets[it], endOffsets[it]) } + + val rangesToDrop = ArrayList() + for (i in startOffsets.indices) { + val start = startOffsets[i] + val end = endOffsets[i] + val range = TextRange(start, end) + + val startToken = file.findElementAt(start)!! + val elementToClipLeft = startToken.maximalParentToClip(range) + if (elementToClipLeft != null) { + val elementStart = elementToClipLeft.range.start + if (elementStart < start) { + val clipBound = tryClipLeftSide(elementToClipLeft, start) + if (clipBound != null) { + val rangeToDrop = TextRange(elementStart, clipBound) + if (canDropRange(rangeToDrop)) { + rangesToDrop.add(rangeToDrop) + } + } + } + } + + val endToken = file.findElementAt(end - 1)!! + val elementToClipRight = endToken.maximalParentToClip(range) + if (elementToClipRight != null) { + val elementEnd = elementToClipRight.range.end + if (elementEnd > end) { + val clipBound = tryClipRightSide(elementToClipRight, end) + if (clipBound != null) { + val rangeToDrop = TextRange(clipBound, elementEnd) + if (canDropRange(rangeToDrop)) { + rangesToDrop.add(rangeToDrop) + } + } + } + } + } + + if (rangesToDrop.isEmpty()) return null + + val newFileText = StringBuilder { + var offset = 0 + for (range in rangesToDrop) { + assert(range.start >= offset) + append(fileText.substring(offset, range.start)) + offset = range.end + } + append(fileText.substring(offset, fileText.length())) + }.toString() + + fun IntArray.update() { + for (range in rangesToDrop.reverse()) { + for (i in indices) { + val offset = this[i] + if (offset >= range.end) { + this[i] = offset - range.length + } + else { + assert(offset <= range.start) + } + } + } + } + + startOffsets.update() + endOffsets.update() + + return newFileText + } + + private fun PsiElement.maximalParentToClip(range: TextRange): PsiElement? { + val firstNotInRange = parents().takeWhile { it !is PsiDirectory }.firstOrNull { it.range !in range } ?: return null + return firstNotInRange.parents().lastOrNull { it.minimizedTextRange() in range } + } + + private fun PsiElement.minimizedTextRange(): TextRange { + val firstChild = getFirstChild()?.siblings()?.firstOrNull { !canDropElementFromText(it) } ?: return range + val lastChild = getLastChild().siblings(forward = false).first { !canDropElementFromText(it) } + return TextRange(firstChild.minimizedTextRange().start, lastChild.minimizedTextRange().end) + } + + // element's text can be removed from file's text keeping parsing the same + private fun canDropElementFromText(element: PsiElement): Boolean { + return when (element) { + is PsiWhiteSpace, is PsiComment, is PsiModifierList, + is PsiAnnotation, is PsiPackageStatement, is PsiImportList, is PsiImportStatementBase -> true + + is PsiJavaToken -> { + when (element.getTokenType()) { + // modifiers + JavaTokenType.PUBLIC_KEYWORD, JavaTokenType.PROTECTED_KEYWORD, JavaTokenType.PRIVATE_KEYWORD, + JavaTokenType.STATIC_KEYWORD, JavaTokenType.ABSTRACT_KEYWORD, JavaTokenType.FINAL_KEYWORD, + JavaTokenType.NATIVE_KEYWORD, JavaTokenType.SYNCHRONIZED_KEYWORD, JavaTokenType.STRICTFP_KEYWORD, + JavaTokenType.TRANSIENT_KEYWORD, JavaTokenType.VOLATILE_KEYWORD, JavaTokenType.DEFAULT_KEYWORD -> element.getParent() is PsiModifierList + + JavaTokenType.SEMICOLON -> true + + else -> false + } + } + + is PsiCodeBlock -> element.getParent() is PsiMethod + + else -> element.getFirstChild() == null + } + } + + private fun tryClipLeftSide(element: PsiElement, leftBound: Int) + = tryClipSide(element, leftBound, { range }, { getFirstChild().siblings() }) + + private fun tryClipRightSide(element: PsiElement, rightBound: Int): Int? { + fun Int.transform() = Int.MAX_VALUE - this + fun TextRange.transform() = TextRange(end.transform(), start.transform()) + return tryClipSide(element, rightBound.transform(), { range.transform() }, { getLastChild().siblings(forward = false) })?.transform() + } + + private fun tryClipSide( + element: PsiElement, + rangeBound: Int, + rangeFunction: PsiElement.() -> TextRange, + childrenFunction: PsiElement.() -> Sequence + ): Int? { + if (element.getFirstChild() == null) return null + + val elementRange = element.rangeFunction() + assert(elementRange.start < rangeBound && rangeBound < elementRange.end) + + var clipTo = elementRange.start + for (child in element.childrenFunction()) { + val childRange = child.rangeFunction() + + if (childRange.start >= rangeBound) { // we have cut enough already + break + } + else if (childRange.end <= rangeBound) { // need to drop the whole element + if (!canDropElementFromText(child)) return null + clipTo = childRange.end + } + else { // rangeBound is inside element's range + if (child is PsiWhiteSpace) break // no need to cut whitespace - we can leave it as is + return tryClipSide(child, rangeBound, rangeFunction, childrenFunction) + } + } + + return clipTo + } + private fun MutableList.collectElementsToConvert( file: PsiJavaFile, fileText: String, @@ -53,12 +213,12 @@ data class DataForConversion private( ) { val elements = file.elementsInRange(range) if (elements.isEmpty()) { - add(fileText.substring(range.getStartOffset(), range.getEndOffset())) + add(fileText.substring(range.start, range.end)) } else { - add(fileText.substring(range.getStartOffset(), elements.first().getTextRange().getStartOffset())) + add(fileText.substring(range.start, elements.first().range.start)) addAll(elements) - add(fileText.substring(elements.last().getTextRange().getEndOffset(), range.getEndOffset())) + add(fileText.substring(elements.last().range.end, range.end)) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/RangeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/RangeUtils.kt index a8a54f6dda8..f8765b3bc9a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/RangeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/RangeUtils.kt @@ -28,6 +28,9 @@ public val TextRange.start: Int public val TextRange.end: Int get() = getEndOffset() +public val TextRange.length: Int + get() = getLength() + public val PsiElement.range: TextRange get() = getTextRange()!! diff --git a/idea/testData/copyPaste/conversion/ClassWithNoDocComment.expected.kt b/idea/testData/copyPaste/conversion/ClassWithNoDocComment.expected.kt new file mode 100644 index 00000000000..11ae5fc5a80 --- /dev/null +++ b/idea/testData/copyPaste/conversion/ClassWithNoDocComment.expected.kt @@ -0,0 +1,6 @@ +package to + +public class JavaClass : Runnable { + override fun run() { + } +} diff --git a/idea/testData/copyPaste/conversion/ClassWithNoDocComment.java b/idea/testData/copyPaste/conversion/ClassWithNoDocComment.java new file mode 100644 index 00000000000..1c2577f2f52 --- /dev/null +++ b/idea/testData/copyPaste/conversion/ClassWithNoDocComment.java @@ -0,0 +1,10 @@ +/** + * Doc comment for JavaClass + */ +public class JavaClass implements Runnable{ + @Override + public void run() { + + } +} + \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/FieldWithNoEndComment.expected.kt b/idea/testData/copyPaste/conversion/FieldWithNoEndComment.expected.kt new file mode 100644 index 00000000000..6e9e8d02505 --- /dev/null +++ b/idea/testData/copyPaste/conversion/FieldWithNoEndComment.expected.kt @@ -0,0 +1,3 @@ +package to + +private val field = 1 diff --git a/idea/testData/copyPaste/conversion/FieldWithNoEndComment.java b/idea/testData/copyPaste/conversion/FieldWithNoEndComment.java new file mode 100644 index 00000000000..1d8e27e4329 --- /dev/null +++ b/idea/testData/copyPaste/conversion/FieldWithNoEndComment.java @@ -0,0 +1,3 @@ +public class JavaClass { + private int field = 1; // comment for field +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.expected.kt b/idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.expected.kt new file mode 100644 index 00000000000..fdf307209df --- /dev/null +++ b/idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.expected.kt @@ -0,0 +1,3 @@ +package to + +volatile var field = 1 diff --git a/idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.java b/idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.java new file mode 100644 index 00000000000..f859155d2d2 --- /dev/null +++ b/idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.java @@ -0,0 +1,3 @@ +public class JavaClass { + public volatile int field = 1; +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/FileWithNoPackageStatement.expected.kt b/idea/testData/copyPaste/conversion/FileWithNoPackageStatement.expected.kt new file mode 100644 index 00000000000..298f905764b --- /dev/null +++ b/idea/testData/copyPaste/conversion/FileWithNoPackageStatement.expected.kt @@ -0,0 +1,9 @@ +package to + +public object JavaClass { + public fun main(args: Array) { + println("Hello, world!") + } +} + +fun main(args: Array) = JavaClass.main(args) diff --git a/idea/testData/copyPaste/conversion/FileWithNoPackageStatement.java b/idea/testData/copyPaste/conversion/FileWithNoPackageStatement.java new file mode 100644 index 00000000000..e8a90ab9dca --- /dev/null +++ b/idea/testData/copyPaste/conversion/FileWithNoPackageStatement.java @@ -0,0 +1,10 @@ +// this test should cause the whole file to be converted at once (with non-selected text stripped) and the main function to be generated +package helloWorld; + +// sample class +public class JavaClass { + public static void main(String[] args) { + System.out.println("Hello, world!"); + } +} + \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.expected.kt b/idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.expected.kt new file mode 100644 index 00000000000..bc15168ac57 --- /dev/null +++ b/idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.expected.kt @@ -0,0 +1,3 @@ +package to + +fun foo(p: Int) diff --git a/idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.java b/idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.java new file mode 100644 index 00000000000..f9261dcfb6f --- /dev/null +++ b/idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.java @@ -0,0 +1,5 @@ +class A { + public void foo(int p) { + System.out.println("foo"); + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/MethodWithNoAnnotation.expected.kt b/idea/testData/copyPaste/conversion/MethodWithNoAnnotation.expected.kt new file mode 100644 index 00000000000..738ccbff007 --- /dev/null +++ b/idea/testData/copyPaste/conversion/MethodWithNoAnnotation.expected.kt @@ -0,0 +1,5 @@ +package to + +public fun foo(): String { + return "" +} diff --git a/idea/testData/copyPaste/conversion/MethodWithNoAnnotation.java b/idea/testData/copyPaste/conversion/MethodWithNoAnnotation.java new file mode 100644 index 00000000000..1bcceb4591e --- /dev/null +++ b/idea/testData/copyPaste/conversion/MethodWithNoAnnotation.java @@ -0,0 +1,6 @@ +public class JavaClass { + @Deprecated + public String foo() { + return ""; + } +} diff --git a/idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.expected.kt b/idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.expected.kt new file mode 100644 index 00000000000..e8c6629a30c --- /dev/null +++ b/idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.expected.kt @@ -0,0 +1,6 @@ +package to + +SomeAnnotation +public fun foo(): String { + return "" +} diff --git a/idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.java b/idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.java new file mode 100644 index 00000000000..d5cbe9808a4 --- /dev/null +++ b/idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.java @@ -0,0 +1,7 @@ +public class JavaClass { + @Deprecated + @SomeAnnotation + public String foo() { + return ""; + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java index 95f98b34eba..618f174b2f2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.idea.conversion.copy; import com.intellij.testFramework.TestDataPath; -import org.jetbrains.kotlin.test.InnerTestClasses; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.JetTestUtils; import org.jetbrains.kotlin.test.TestMetadata; @@ -72,6 +71,12 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("ClassWithNoDocComment.java") + public void testClassWithNoDocComment() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/ClassWithNoDocComment.java"); + doTest(fileName); + } + @TestMetadata("Constructor.java") public void testConstructor() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/Constructor.java"); @@ -84,6 +89,24 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("FieldWithNoEndComment.java") + public void testFieldWithNoEndComment() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/FieldWithNoEndComment.java"); + doTest(fileName); + } + + @TestMetadata("FieldWithNoModifierAndNoSemicolon.java") + public void testFieldWithNoModifierAndNoSemicolon() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/FieldWithNoModifierAndNoSemicolon.java"); + doTest(fileName); + } + + @TestMetadata("FileWithNoPackageStatement.java") + public void testFileWithNoPackageStatement() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/FileWithNoPackageStatement.java"); + doTest(fileName); + } + @TestMetadata("HalfTheWhiteSpace.java") public void testHalfTheWhiteSpace() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/HalfTheWhiteSpace.java"); @@ -114,12 +137,30 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("MethodDeclarationWithNoBody.java") + public void testMethodDeclarationWithNoBody() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/MethodDeclarationWithNoBody.java"); + doTest(fileName); + } + @TestMetadata("MethodReferenceWithoutQualifier.java") public void testMethodReferenceWithoutQualifier() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/MethodReferenceWithoutQualifier.java"); doTest(fileName); } + @TestMetadata("MethodWithNoAnnotation.java") + public void testMethodWithNoAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/MethodWithNoAnnotation.java"); + doTest(fileName); + } + + @TestMetadata("MethodWithOnlyOneAnnotation.java") + public void testMethodWithOnlyOneAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/MethodWithOnlyOneAnnotation.java"); + doTest(fileName); + } + @TestMetadata("OnlyClosingBrace.java") public void testOnlyClosingBrace() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/OnlyClosingBrace.java");