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 fbd7a73c9d7..4f6200d2d50 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 @@ -28,9 +28,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler -import org.jetbrains.kotlin.idea.util.FuzzyType -import org.jetbrains.kotlin.idea.util.findLabelAndCall -import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression +import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parents @@ -41,6 +39,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.inline.InlineUtil +import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.checker.JetTypeChecker import org.jetbrains.kotlin.types.typeUtil.equalTypesOrNulls @@ -223,11 +222,12 @@ fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): val result = ArrayList() for (parent in position.parentsWithSelf) { if (parent is JetDeclarationWithBody) { - val returnsUnit = returnsUnit(parent, bindingContext) + val returnType = parent.returnType(bindingContext) + val isUnit = returnType == null || KotlinBuiltIns.isUnit(returnType) if (parent is JetFunctionLiteral) { val (label, call) = parent.findLabelAndCall() if (label != null) { - result.add(createKeywordWithLabelElement("return", label, addSpace = !returnsUnit)) + result.add(createKeywordWithLabelElement("return", label, addSpace = !isUnit)) } // check if the current function literal is inlined and stop processing outer declarations if it's not @@ -236,7 +236,15 @@ fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): } else { if (parent.hasBlockBody()) { - result.add(createKeywordWithLabelElement("return", null, addSpace = !returnsUnit)) + result.add(createKeywordWithLabelElement("return", null, addSpace = !isUnit)) + + if (returnType != null && returnType.nullability() == TypeNullability.NULLABLE) { + result.add(createKeywordWithLabelElement("return null", null, addSpace = false)) + } + if (returnType != null && KotlinBuiltIns.isBoolean(returnType.makeNotNullable())) { + result.add(createKeywordWithLabelElement("return true", null, addSpace = false)) + result.add(createKeywordWithLabelElement("return false", null, addSpace = false)) + } } break } @@ -245,10 +253,9 @@ fun returnExpressionItems(bindingContext: BindingContext, position: JetElement): return result } -private fun returnsUnit(declaration: JetDeclarationWithBody, bindingContext: BindingContext): Boolean { - val callable = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] as? CallableDescriptor ?: return true - val returnType = callable.getReturnType() ?: return true - return KotlinBuiltIns.isUnit(returnType) +private fun JetDeclarationWithBody.returnType(bindingContext: BindingContext): JetType? { + val callable = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? CallableDescriptor ?: return null + return callable.getReturnType() } private fun createKeywordWithLabelElement(keyword: String, label: Name?, addSpace: Boolean): LookupElement { diff --git a/idea/idea-completion/testData/handlers/keywords/ReturnNull.kt b/idea/idea-completion/testData/handlers/keywords/ReturnNull.kt new file mode 100644 index 00000000000..3974afe9d0e --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/ReturnNull.kt @@ -0,0 +1,5 @@ +fun foo(): String? { + ret +} + +// ELEMENT: "return null" diff --git a/idea/idea-completion/testData/handlers/keywords/ReturnNull.kt.after b/idea/idea-completion/testData/handlers/keywords/ReturnNull.kt.after new file mode 100644 index 00000000000..babf2fdb075 --- /dev/null +++ b/idea/idea-completion/testData/handlers/keywords/ReturnNull.kt.after @@ -0,0 +1,5 @@ +fun foo(): String? { + return null +} + +// ELEMENT: "return null" diff --git a/idea/idea-completion/testData/keywords/ReturnBoolean.kt b/idea/idea-completion/testData/keywords/ReturnBoolean.kt new file mode 100644 index 00000000000..0310379cb2b --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnBoolean.kt @@ -0,0 +1,9 @@ +fun foo(): Boolean { + ret +} + +// INVOCATION_COUNT: 1 +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return true", itemText: "return true", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return false", itemText: "return false", tailText: null, attributes: "bold" } +// NOTHING_ELSE: true diff --git a/idea/idea-completion/testData/keywords/ReturnNotNull.kt b/idea/idea-completion/testData/keywords/ReturnNotNull.kt new file mode 100644 index 00000000000..43f1d77e1c5 --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnNotNull.kt @@ -0,0 +1,8 @@ +fun foo(): String { + ret +} + +// INVOCATION_COUNT: 1 +// ABSENT: "return null" +// ABSENT: "return true" +// ABSENT: "return false" diff --git a/idea/idea-completion/testData/keywords/ReturnNull.kt b/idea/idea-completion/testData/keywords/ReturnNull.kt new file mode 100644 index 00000000000..288b355fe75 --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnNull.kt @@ -0,0 +1,8 @@ +fun foo(): String? { + ret +} + +// INVOCATION_COUNT: 1 +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return null", itemText: "return null", tailText: null, attributes: "bold" } +// NOTHING_ELSE: true diff --git a/idea/idea-completion/testData/keywords/ReturnNullableBoolean.kt b/idea/idea-completion/testData/keywords/ReturnNullableBoolean.kt new file mode 100644 index 00000000000..7467309a69a --- /dev/null +++ b/idea/idea-completion/testData/keywords/ReturnNullableBoolean.kt @@ -0,0 +1,10 @@ +fun foo(): Boolean? { + ret +} + +// INVOCATION_COUNT: 1 +// EXIST: { lookupString: "return", itemText: "return", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return null", itemText: "return null", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return true", itemText: "return true", tailText: null, attributes: "bold" } +// EXIST: { lookupString: "return false", itemText: "return false", tailText: null, attributes: "bold" } +// NOTHING_ELSE: true diff --git a/idea/idea-completion/testData/weighers/basic/ReturnBoolean.kt b/idea/idea-completion/testData/weighers/basic/ReturnBoolean.kt new file mode 100644 index 00000000000..5ece2f86d1a --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/ReturnBoolean.kt @@ -0,0 +1,7 @@ +fun foo(): Boolean { + ret +} + +// ORDER: return +// ORDER: return false +// ORDER: return true diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java index 844e19c7ef0..29ed701a096 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KeywordCompletionTestGenerated.java @@ -401,12 +401,36 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes doTest(fileName); } + @TestMetadata("ReturnBoolean.kt") + public void testReturnBoolean() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnBoolean.kt"); + doTest(fileName); + } + @TestMetadata("ReturnKeywordName.kt") public void testReturnKeywordName() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnKeywordName.kt"); doTest(fileName); } + @TestMetadata("ReturnNotNull.kt") + public void testReturnNotNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("ReturnNull.kt") + public void testReturnNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnNull.kt"); + doTest(fileName); + } + + @TestMetadata("ReturnNullableBoolean.kt") + public void testReturnNullableBoolean() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ReturnNullableBoolean.kt"); + doTest(fileName); + } + @TestMetadata("This.kt") public void testThis() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/This.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java index eb3d7049069..f8562388fbf 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/KeywordCompletionHandlerTestGenerated.java @@ -107,6 +107,12 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple doTest(fileName); } + @TestMetadata("ReturnNull.kt") + public void testReturnNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/ReturnNull.kt"); + doTest(fileName); + } + @TestMetadata("SpaceAfterImport.kt") public void testSpaceAfterImport() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/SpaceAfterImport.kt"); 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 141f26f992c..2ff93c7cd63 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 @@ -124,4 +124,10 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/PropertiesBeforeKeywords.kt"); doTest(fileName); } + + @TestMetadata("ReturnBoolean.kt") + public void testReturnBoolean() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/ReturnBoolean.kt"); + doTest(fileName); + } }