From faf4c05fc8a5867f57170c34d7e0a942419ba1fc Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Fri, 8 Nov 2019 18:23:58 +0300 Subject: [PATCH] KT-34644 Give `return` keyword more priority in completion where appropriate - See `returnExpressionItems::returnIsProbableInPosition` to get an idea where return is supposed to be more appropriate - End of the block (except for top level block of unit function or cycle) or as single expression in if or when - Right side of the elvis operator (which is not already in return) - Add additional `probable_keyword` weigher to signalize about probable keyword (for now it is only for `return`) - ^KT-34644 Fixed --- .../kotlin/idea/completion/CompletionUtils.kt | 73 ++++++- .../kotlin/idea/completion/Weighers.kt | 3 +- .../kotlin/idea/completion/smart/Utils.kt | 3 + .../noReturnType/BeginOfNestedBlock.kt | 11 ++ .../noReturnType/BeginOfTopLevelBlock.kt | 9 + .../noReturnType/EndOfNestedBlock.kt | 10 + .../noReturnType/EndOfTopLevelBlock.kt | 8 + .../noReturnType/ForWithBody.kt | 10 + .../noReturnType/ForWithoutBody.kt | 8 + .../noReturnType/IfWithoutBody.kt | 8 + .../contextualReturn/noReturnType/InElvis.kt | 9 + .../InElvisWhenSmartCompletionWins.kt | 12 ++ .../noReturnType/InWhenSingleExpression.kt | 11 ++ .../noReturnType/InWhenWithBody.kt | 13 ++ .../withReturnType/BeginOfNestedBlock.kt | 13 ++ .../withReturnType/BeginOfTopLevelBlock.kt | 9 + .../withReturnType/EndOfNestedBlock.kt | 12 ++ .../withReturnType/EndOfTopLevelBlock.kt | 8 + .../withReturnType/ForWithBody.kt | 12 ++ .../withReturnType/ForWithoutBody.kt | 10 + .../withReturnType/IfWithoutBody.kt | 10 + .../withReturnType/InElvis.kt | 9 + .../withReturnType/InElvisInReturn.kt | 8 + .../InElvisWhenSmartCompletionWins.kt | 13 ++ .../InIfAsReturnedExpression.kt | 8 + .../InIfInWhenWithBodyAsReturnedExpression.kt | 16 ++ .../InNotElvisBinaryOperator.kt | 9 + .../InWhenAsReturnedExpression.kt | 11 ++ .../withReturnType/InWhenSingleExpression.kt | 11 ++ .../withReturnType/InWhenWithBody.kt | 13 ++ .../InWhenWithBodyAsReturnedExpression.kt | 11 ++ .../BasicCompletionWeigherTestGenerated.java | 179 ++++++++++++++++++ 32 files changed, 546 insertions(+), 4 deletions(-) create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfNestedBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfTopLevelBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfNestedBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfTopLevelBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithoutBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/IfWithoutBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvis.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvisWhenSmartCompletionWins.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenSingleExpression.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenWithBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfNestedBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfTopLevelBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfNestedBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfTopLevelBlock.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithoutBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/IfWithoutBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvis.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisInReturn.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisWhenSmartCompletionWins.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfAsReturnedExpression.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfInWhenWithBodyAsReturnedExpression.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InNotElvisBinaryOperator.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenAsReturnedExpression.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenSingleExpression.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBody.kt create mode 100644 idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBodyAsReturnedExpression.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt index 0e619d9cf7d..b2431e34ee5 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt @@ -14,22 +14,23 @@ import com.intellij.openapi.util.Key import com.intellij.patterns.ElementPattern import com.intellij.patterns.StandardPatterns import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler +import org.jetbrains.kotlin.idea.completion.smart.isProbableKeyword import org.jetbrains.kotlin.idea.core.ImportableFqNameClassifier import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.* +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf -import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -223,6 +224,22 @@ fun returnExpressionItems(bindingContext: BindingContext, position: KtElement): } } + val isOnTopLevelInUnitFunction = isUnit && position.parent?.parent === parent + + val isInsideLambda = position.getNonStrictParentOfType()?.let { parent.isAncestor(it) } == true + + fun returnIsProbableInPosition(): Boolean = when { + isInsideLambda -> false // for now we do not want to alter completion inside lambda bodies + position.inReturnExpression() -> false + position.isRightOperandInElvis() -> true + position.isLastOrSingleStatement() && !position.isDirectlyInLoopBody() && !isOnTopLevelInUnitFunction -> true + else -> false + } + + if (returnIsProbableInPosition()) { + blockBodyReturns.forEach { it.isProbableKeyword = true } + } + result.addAll(blockBodyReturns) } break @@ -232,6 +249,56 @@ fun returnExpressionItems(bindingContext: BindingContext, position: KtElement): return result } +private fun KtElement.isDirectlyInLoopBody(): Boolean { + val possibleLoop = when (parent) { + is KtBlockExpression -> parent.parent?.parent + is KtContainerNodeForControlStructureBody -> parent.parent + else -> null + } + + return possibleLoop is KtLoopExpression +} + +fun KtElement.isRightOperandInElvis(): Boolean { + val elvisParent = parent as? KtBinaryExpression ?: return false + return elvisParent.operationToken == KtTokens.ELVIS && elvisParent.right === this +} + +/** + * Checks if expression is either last expression in a block, or a single expression in position where single + * expressions are allowed (`when` entries, `for` and `while` loops, and `if`s). + */ +private fun PsiElement.isLastOrSingleStatement(): Boolean = + when (val containingExpression = parent) { + is KtBlockExpression -> containingExpression.statements.lastOrNull() === this + is KtWhenEntry, is KtContainerNodeForControlStructureBody -> true + else -> false + } + +private fun KtElement.inReturnExpression(): Boolean = findReturnExpression(this) != null + +/** + * If [expression] is directly relates to the return expression already, this return expression will be found. + * + * Examples: + * + * ```kotlin + * return 10 // 10 is in return + * return if (true) 10 else 20 // 10 and 20 are in return + * return 10 ?: 20 // 10 and 20 are in return + * return when { true -> 10 ; else -> { 20; 30 } } // 10 and 30 are in return, but 20 is not + * ``` + */ +private tailrec fun findReturnExpression(expression: PsiElement?): KtReturnExpression? = + when (val parent = expression?.parent) { + is KtReturnExpression -> parent + is KtBinaryExpression -> findReturnExpression(parent.takeIf { it.operationToken == KtTokens.ELVIS }) + is KtContainerNodeForControlStructureBody, is KtIfExpression -> findReturnExpression(parent) + is KtBlockExpression -> findReturnExpression(parent.takeIf { expression.isLastOrSingleStatement() }) + is KtWhenEntry -> findReturnExpression(parent.parent) + else -> null + } + private fun KtDeclarationWithBody.returnType(bindingContext: BindingContext): KotlinType? { val callable = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? CallableDescriptor ?: return null return callable.returnType diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt index 4abed12ea1c..e256b570bfb 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt @@ -115,6 +115,7 @@ object SmartCompletionPriorityWeigher : LookupElementWeigher("kotlin.smartComple object KindWeigher : LookupElementWeigher("kotlin.kind") { private enum class Weight { + probableKeyword, enumMember, callable, keyword, @@ -137,7 +138,7 @@ object KindWeigher : LookupElementWeigher("kotlin.kind") { } } - is KeywordLookupObject -> Weight.keyword + is KeywordLookupObject -> if (element.isProbableKeyword) Weight.probableKeyword else Weight.keyword else -> Weight.default } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index dc939b08185..3aac49a5dad 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.completion.suppressAutoInsertion import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.* +import org.jetbrains.kotlin.psi.NotNullableUserDataProperty import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver @@ -298,6 +299,8 @@ fun LookupElement.assignSmartCompletionPriority(priority: SmartCompletionItemPri return this } +var LookupElement.isProbableKeyword: Boolean by NotNullableUserDataProperty(Key.create("PROBABLE_KEYWORD_KEY"), false) + fun DeclarationDescriptor.fuzzyTypesForSmartCompletion( smartCastCalculator: SmartCastCalculator, callTypeAndReceiver: CallTypeAndReceiver<*, *>, diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfNestedBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfNestedBlock.kt new file mode 100644 index 00000000000..287fb10b056 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfNestedBlock.kt @@ -0,0 +1,11 @@ +fun returnFun() {} + +fun usage() { + if (true) { + re + return + } +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfTopLevelBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfTopLevelBlock.kt new file mode 100644 index 00000000000..40fe046be2d --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfTopLevelBlock.kt @@ -0,0 +1,9 @@ +fun returnFun() {} + +fun usage() { + re + return +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfNestedBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfNestedBlock.kt new file mode 100644 index 00000000000..c5326b15e26 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfNestedBlock.kt @@ -0,0 +1,10 @@ +fun returnFun() {} + +fun usage() { + if (true) { + re + } +} + +// ORDER: return +// ORDER: returnFun \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfTopLevelBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfTopLevelBlock.kt new file mode 100644 index 00000000000..897b9cc9a9b --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfTopLevelBlock.kt @@ -0,0 +1,8 @@ +fun returnFun() {} + +fun usage() { + re +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithBody.kt new file mode 100644 index 00000000000..3a2bee48ddc --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithBody.kt @@ -0,0 +1,10 @@ +fun returnFun() {} + +fun usage() { + for (i in 1..10) { + re + } +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithoutBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithoutBody.kt new file mode 100644 index 00000000000..0339f7f8e29 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithoutBody.kt @@ -0,0 +1,8 @@ +fun returnFun() {} + +fun usage() { + for (i in 1..10) re +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/IfWithoutBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/IfWithoutBody.kt new file mode 100644 index 00000000000..61793cbe47d --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/IfWithoutBody.kt @@ -0,0 +1,8 @@ +fun returnFun() {} + +fun usage() { + if (true) re +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvis.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvis.kt new file mode 100644 index 00000000000..2c13b290b73 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvis.kt @@ -0,0 +1,9 @@ +fun returnFun() {} + +fun usage(a: Int?) { + a ?: re + return +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvisWhenSmartCompletionWins.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvisWhenSmartCompletionWins.kt new file mode 100644 index 00000000000..64e9cbb238e --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvisWhenSmartCompletionWins.kt @@ -0,0 +1,12 @@ +fun returnFun(): Int = 10 +fun returnAnything(): T = null!! + +fun usage(a: Int?) { + a ?: re +} + +// function of the same type as `a` is preferred to return in this case + +// ORDER: returnFun +// ORDER: return +// ORDER: returnAnything \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenSingleExpression.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenSingleExpression.kt new file mode 100644 index 00000000000..4bd814076fb --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenSingleExpression.kt @@ -0,0 +1,11 @@ +fun returnFun() {} + +fun usage(a: Int) { + when (a) { + 10 -> re + } + return +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenWithBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenWithBody.kt new file mode 100644 index 00000000000..c946a86258a --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenWithBody.kt @@ -0,0 +1,13 @@ +fun returnFun() {} + +fun usage(a: Int) { + when (a) { + 10 -> { + re + } + } + return +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfNestedBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfNestedBlock.kt new file mode 100644 index 00000000000..dbe5dd2ab1d --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfNestedBlock.kt @@ -0,0 +1,13 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + if (true) { + re + return 20 + } + + return 10 +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfTopLevelBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfTopLevelBlock.kt new file mode 100644 index 00000000000..542bb01f4b0 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfTopLevelBlock.kt @@ -0,0 +1,9 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + re + return 10 +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfNestedBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfNestedBlock.kt new file mode 100644 index 00000000000..b4964df6fcc --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfNestedBlock.kt @@ -0,0 +1,12 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + if (true) { + re + } + + return 10 +} + +// ORDER: return +// ORDER: returnFun \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfTopLevelBlock.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfTopLevelBlock.kt new file mode 100644 index 00000000000..e8ab4515f03 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfTopLevelBlock.kt @@ -0,0 +1,8 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + re +} + +// ORDER: return +// ORDER: returnFun \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithBody.kt new file mode 100644 index 00000000000..2117788c584 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithBody.kt @@ -0,0 +1,12 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + for (i in 1..10) { + re + } + + return 10 +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithoutBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithoutBody.kt new file mode 100644 index 00000000000..82f905cc8cf --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithoutBody.kt @@ -0,0 +1,10 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + for (i in 1..10) re + + return 10 +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/IfWithoutBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/IfWithoutBody.kt new file mode 100644 index 00000000000..5966b139357 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/IfWithoutBody.kt @@ -0,0 +1,10 @@ +fun returnFun(): Int = 10 + +fun usage(): Int { + if (true) re + + return 10 +} + +// ORDER: return +// ORDER: returnFun \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvis.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvis.kt new file mode 100644 index 00000000000..64b27d9d79c --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvis.kt @@ -0,0 +1,9 @@ +fun returnFun() {} + +fun usage(a: Int?): Int { + a ?: re + return 10 +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisInReturn.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisInReturn.kt new file mode 100644 index 00000000000..4ec2f55f09d --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisInReturn.kt @@ -0,0 +1,8 @@ +fun reportError(): Nothing + +fun usage(a: Int?): Int { + return a ?: a ?: a ?: re +} + +// ORDER: reportError +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisWhenSmartCompletionWins.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisWhenSmartCompletionWins.kt new file mode 100644 index 00000000000..94783a138bb --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisWhenSmartCompletionWins.kt @@ -0,0 +1,13 @@ +fun returnFun(): Int = 10 +fun returnAnything(): T = null!! + +fun usage(a: Int?): Int { + a ?: re + return 10 +} + +// function of the same type as `a` is preferred to return in this case + +// ORDER: returnFun +// ORDER: return +// ORDER: returnAnything \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfAsReturnedExpression.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfAsReturnedExpression.kt new file mode 100644 index 00000000000..d40a523b7a9 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfAsReturnedExpression.kt @@ -0,0 +1,8 @@ +fun reportError(): Nothing + +fun usage(a: Int?): Int { + return if (a == null) re else a +} + +// ORDER: reportError +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfInWhenWithBodyAsReturnedExpression.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfInWhenWithBodyAsReturnedExpression.kt new file mode 100644 index 00000000000..fe78113d7bc --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfInWhenWithBodyAsReturnedExpression.kt @@ -0,0 +1,16 @@ +fun reportError(): Nothing + +fun usage(a: Int?): Int { + return when { + a == null -> { + if (true) { re } + + 10 + } + + else -> a + } +} + +// ORDER: return +// ORDER: reportError diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InNotElvisBinaryOperator.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InNotElvisBinaryOperator.kt new file mode 100644 index 00000000000..8fa85626aa2 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InNotElvisBinaryOperator.kt @@ -0,0 +1,9 @@ +fun returnFun(): String = "" + +fun usage(a: Int?): Int { + a to re + return 10 +} + +// ORDER: returnFun +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenAsReturnedExpression.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenAsReturnedExpression.kt new file mode 100644 index 00000000000..760af7e23c2 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenAsReturnedExpression.kt @@ -0,0 +1,11 @@ +fun reportError(): Nothing + +fun usage(a: Int?): Int { + return when { + a == null -> re + else -> a + } +} + +// ORDER: reportError +// ORDER: return diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenSingleExpression.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenSingleExpression.kt new file mode 100644 index 00000000000..48a6978ee8c --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenSingleExpression.kt @@ -0,0 +1,11 @@ +fun returnFun(): Int = 10 + +fun usage(a: Int): Int { + when (a) { + 10 -> re + } + return 10 +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBody.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBody.kt new file mode 100644 index 00000000000..c41a6489e62 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBody.kt @@ -0,0 +1,13 @@ +fun returnFun(): Int = 10 + +fun usage(a: Int): Int { + when (a) { + 10 -> { + re + } + } + return 10 +} + +// ORDER: return +// ORDER: returnFun diff --git a/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBodyAsReturnedExpression.kt b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBodyAsReturnedExpression.kt new file mode 100644 index 00000000000..760af7e23c2 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBodyAsReturnedExpression.kt @@ -0,0 +1,11 @@ +fun reportError(): Nothing + +fun usage(a: Int?): Int { + return when { + a == null -> re + else -> a + } +} + +// ORDER: reportError +// ORDER: return diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java index 9bb1ffca775..37c28123691 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java @@ -213,6 +213,185 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion runTest("idea/idea-completion/testData/weighers/basic/UnavailableDslReceiver.kt"); } + @TestMetadata("idea/idea-completion/testData/weighers/basic/contextualReturn") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ContextualReturn extends AbstractBasicCompletionWeigherTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInContextualReturn() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/contextualReturn"), Pattern.compile("^([^.]+)\\.(kt|kts)$"), true); + } + + @TestMetadata("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoReturnType extends AbstractBasicCompletionWeigherTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInNoReturnType() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType"), Pattern.compile("^([^.]+)\\.(kt|kts)$"), true); + } + + @TestMetadata("BeginOfNestedBlock.kt") + public void testBeginOfNestedBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfNestedBlock.kt"); + } + + @TestMetadata("BeginOfTopLevelBlock.kt") + public void testBeginOfTopLevelBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/BeginOfTopLevelBlock.kt"); + } + + @TestMetadata("EndOfNestedBlock.kt") + public void testEndOfNestedBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfNestedBlock.kt"); + } + + @TestMetadata("EndOfTopLevelBlock.kt") + public void testEndOfTopLevelBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/EndOfTopLevelBlock.kt"); + } + + @TestMetadata("ForWithBody.kt") + public void testForWithBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithBody.kt"); + } + + @TestMetadata("ForWithoutBody.kt") + public void testForWithoutBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/ForWithoutBody.kt"); + } + + @TestMetadata("IfWithoutBody.kt") + public void testIfWithoutBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/IfWithoutBody.kt"); + } + + @TestMetadata("InElvis.kt") + public void testInElvis() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvis.kt"); + } + + @TestMetadata("InElvisWhenSmartCompletionWins.kt") + public void testInElvisWhenSmartCompletionWins() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InElvisWhenSmartCompletionWins.kt"); + } + + @TestMetadata("InWhenSingleExpression.kt") + public void testInWhenSingleExpression() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenSingleExpression.kt"); + } + + @TestMetadata("InWhenWithBody.kt") + public void testInWhenWithBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/noReturnType/InWhenWithBody.kt"); + } + } + + @TestMetadata("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithReturnType extends AbstractBasicCompletionWeigherTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInWithReturnType() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType"), Pattern.compile("^([^.]+)\\.(kt|kts)$"), true); + } + + @TestMetadata("BeginOfNestedBlock.kt") + public void testBeginOfNestedBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfNestedBlock.kt"); + } + + @TestMetadata("BeginOfTopLevelBlock.kt") + public void testBeginOfTopLevelBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/BeginOfTopLevelBlock.kt"); + } + + @TestMetadata("EndOfNestedBlock.kt") + public void testEndOfNestedBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfNestedBlock.kt"); + } + + @TestMetadata("EndOfTopLevelBlock.kt") + public void testEndOfTopLevelBlock() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/EndOfTopLevelBlock.kt"); + } + + @TestMetadata("ForWithBody.kt") + public void testForWithBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithBody.kt"); + } + + @TestMetadata("ForWithoutBody.kt") + public void testForWithoutBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/ForWithoutBody.kt"); + } + + @TestMetadata("IfWithoutBody.kt") + public void testIfWithoutBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/IfWithoutBody.kt"); + } + + @TestMetadata("InElvis.kt") + public void testInElvis() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvis.kt"); + } + + @TestMetadata("InElvisInReturn.kt") + public void testInElvisInReturn() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisInReturn.kt"); + } + + @TestMetadata("InElvisWhenSmartCompletionWins.kt") + public void testInElvisWhenSmartCompletionWins() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InElvisWhenSmartCompletionWins.kt"); + } + + @TestMetadata("InIfAsReturnedExpression.kt") + public void testInIfAsReturnedExpression() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfAsReturnedExpression.kt"); + } + + @TestMetadata("InIfInWhenWithBodyAsReturnedExpression.kt") + public void testInIfInWhenWithBodyAsReturnedExpression() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InIfInWhenWithBodyAsReturnedExpression.kt"); + } + + @TestMetadata("InNotElvisBinaryOperator.kt") + public void testInNotElvisBinaryOperator() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InNotElvisBinaryOperator.kt"); + } + + @TestMetadata("InWhenAsReturnedExpression.kt") + public void testInWhenAsReturnedExpression() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenAsReturnedExpression.kt"); + } + + @TestMetadata("InWhenSingleExpression.kt") + public void testInWhenSingleExpression() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenSingleExpression.kt"); + } + + @TestMetadata("InWhenWithBody.kt") + public void testInWhenWithBody() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBody.kt"); + } + + @TestMetadata("InWhenWithBodyAsReturnedExpression.kt") + public void testInWhenWithBodyAsReturnedExpression() throws Exception { + runTest("idea/idea-completion/testData/weighers/basic/contextualReturn/withReturnType/InWhenWithBodyAsReturnedExpression.kt"); + } + } + } + @TestMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)