Extraction Engine: Suggest both bounds for nullability-flexible types without nullability annotations. Do not omit return type if inferred one is flexible
#KT-6837 Fixed
This commit is contained in:
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.JETBRAINS_READONLY_ANNOTATION
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
@@ -49,13 +50,13 @@ public fun approximateFlexibleTypes(jetType: JetType, outermost: Boolean = true)
|
||||
// Foo<Bar!>! -> Foo<Bar>?
|
||||
var approximation =
|
||||
if (isCollection)
|
||||
TypeUtils.makeNullableAsSpecified(if (jetType.isMarkedReadOnly()) flexible.upperBound else flexible.lowerBound, outermost)
|
||||
TypeUtils.makeNullableAsSpecified(if (jetType.isAnnotatedReadOnly()) flexible.upperBound else flexible.lowerBound, outermost)
|
||||
else
|
||||
if (outermost) flexible.upperBound else flexible.lowerBound
|
||||
|
||||
approximation = approximateFlexibleTypes(approximation)
|
||||
|
||||
approximation = if (jetType.isMarkedNotNull()) approximation.makeNotNullable() else approximation
|
||||
approximation = if (jetType.isAnnotatedNotNull()) approximation.makeNotNullable() else approximation
|
||||
|
||||
if (approximation.isMarkedNullable() && !flexible.lowerBound.isMarkedNullable() && TypeUtils.isTypeParameter(approximation) && TypeUtils.hasNullableSuperType(approximation)) {
|
||||
approximation = approximation.makeNotNullable()
|
||||
@@ -72,8 +73,9 @@ public fun approximateFlexibleTypes(jetType: JetType, outermost: Boolean = true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun JetType.isMarkedReadOnly() = hasAnnotationMaybeExternal(JETBRAINS_READONLY_ANNOTATION)
|
||||
private fun JetType.isMarkedNotNull() = hasAnnotationMaybeExternal(JETBRAINS_NOT_NULL_ANNOTATION)
|
||||
public fun JetType.isAnnotatedReadOnly(): Boolean = hasAnnotationMaybeExternal(JETBRAINS_READONLY_ANNOTATION)
|
||||
public fun JetType.isAnnotatedNotNull(): Boolean = hasAnnotationMaybeExternal(JETBRAINS_NOT_NULL_ANNOTATION)
|
||||
public fun JetType.isAnnotatedNullable(): Boolean = hasAnnotationMaybeExternal(JETBRAINS_NULLABLE_ANNOTATION)
|
||||
|
||||
private fun JetType.hasAnnotationMaybeExternal(fqName: FqName) = with (getAnnotations()) {
|
||||
findAnnotation(fqName) ?: findExternalAnnotation(fqName)
|
||||
|
||||
@@ -43,14 +43,24 @@ public class ConvertToExpressionBodyIntention : JetSelfTargetingOffsetIndependen
|
||||
}
|
||||
|
||||
override fun applyTo(element: JetDeclarationWithBody, editor: Editor) {
|
||||
applyToInternal(element, editor)
|
||||
applyToInternal(element) {
|
||||
val typeRef = it.getTypeReference()!!
|
||||
val colon = it.getColon()!!
|
||||
val range = TextRange(colon.startOffset, typeRef.endOffset)
|
||||
editor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset())
|
||||
editor.getCaretModel().moveToOffset(range.getEndOffset())
|
||||
}
|
||||
}
|
||||
|
||||
public fun applyTo(declaration: JetDeclarationWithBody) {
|
||||
applyToInternal(declaration, null)
|
||||
public fun applyTo(declaration: JetDeclarationWithBody, canDeleteTypeRef: Boolean) {
|
||||
applyToInternal(declaration) {
|
||||
if (canDeleteTypeRef) {
|
||||
it.deleteChildRange(it.getColon()!!, it.getTypeReference()!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyToInternal(declaration: JetDeclarationWithBody, editor: Editor?) {
|
||||
private fun applyToInternal(declaration: JetDeclarationWithBody, onFinish: (JetCallableDeclaration) -> Unit) {
|
||||
val value = calcValue(declaration)!!
|
||||
|
||||
if (!declaration.hasDeclaredReturnType() && declaration is JetNamedFunction) {
|
||||
@@ -67,18 +77,7 @@ public class ConvertToExpressionBodyIntention : JetSelfTargetingOffsetIndependen
|
||||
body.replace(value)
|
||||
|
||||
if (omitType) {
|
||||
declaration as JetCallableDeclaration
|
||||
|
||||
val typeRef = declaration.getTypeReference()!!
|
||||
val colon = declaration.getColon()!!
|
||||
if (editor != null) {
|
||||
val range = TextRange(colon.startOffset, typeRef.endOffset)
|
||||
editor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset())
|
||||
editor.getCaretModel().moveToOffset(range.getEndOffset())
|
||||
}
|
||||
else {
|
||||
(declaration as PsiElement).deleteChildRange(colon, typeRef)
|
||||
}
|
||||
onFinish(declaration as JetCallableDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-2
@@ -3,7 +3,7 @@
|
||||
<grid id="27dc6" binding="contentPane" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="522" height="318"/>
|
||||
<xy x="20" y="20" width="522" height="396"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -68,7 +68,7 @@
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="5fe96" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="5fe96" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
@@ -125,6 +125,36 @@
|
||||
<visible value="true"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="4b9b1" binding="returnTypePanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="a2273" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="37c42"/>
|
||||
<text value="&Return type:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="b7d50">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<component id="37c42" class="javax.swing.JComboBox" binding="returnTypeBox">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
<vspacer id="cd10f">
|
||||
|
||||
+43
-3
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.core.refactoring.RefactoringPackage;
|
||||
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle;
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*;
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -53,6 +54,8 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
private JPanel functionNamePanel;
|
||||
private NameSuggestionsField functionNameField;
|
||||
private JLabel functionNameLabel;
|
||||
private JComboBox returnTypeBox;
|
||||
private JPanel returnTypePanel;
|
||||
private KotlinParameterTablePanel parameterTablePanel;
|
||||
|
||||
private final Project project;
|
||||
@@ -139,6 +142,40 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
functionNamePanel.add(functionNameField, BorderLayout.CENTER);
|
||||
functionNameLabel.setLabelFor(functionNameField);
|
||||
|
||||
List<JetType> possibleReturnTypes = ExtractionEnginePackage.getPossibleReturnTypes(extractableCodeDescriptor.getControlFlow());
|
||||
if (possibleReturnTypes.size() > 1) {
|
||||
DefaultComboBoxModel returnTypeBoxModel = new DefaultComboBoxModel(possibleReturnTypes.toArray());
|
||||
returnTypeBox.setModel(returnTypeBoxModel);
|
||||
returnTypeBox.setRenderer(
|
||||
new DefaultListCellRenderer() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList list,
|
||||
Object value,
|
||||
int index,
|
||||
boolean isSelected,
|
||||
boolean cellHasFocus
|
||||
) {
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
setText(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType((JetType) value));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
);
|
||||
returnTypeBox.addItemListener(
|
||||
new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(@NotNull ItemEvent e) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
returnTypePanel.getParent().remove(returnTypePanel);
|
||||
}
|
||||
|
||||
boolean enableVisibility = isVisibilitySectionAvailable();
|
||||
visibilityBox.setEnabled(enableVisibility);
|
||||
if (enableVisibility) {
|
||||
@@ -217,7 +254,8 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
getFunctionName(),
|
||||
getVisibility(),
|
||||
parameterTablePanel.getReceiverInfo(),
|
||||
parameterTablePanel.getParameterInfos());
|
||||
parameterTablePanel.getParameterInfos(),
|
||||
(JetType) returnTypeBox.getSelectedItem());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -230,7 +268,8 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
@NotNull String newName,
|
||||
@NotNull String newVisibility,
|
||||
@Nullable KotlinParameterTablePanel.ParameterInfo newReceiverInfo,
|
||||
@NotNull List<KotlinParameterTablePanel.ParameterInfo> newParameterInfos
|
||||
@NotNull List<KotlinParameterTablePanel.ParameterInfo> newParameterInfos,
|
||||
@Nullable JetType returnType
|
||||
) {
|
||||
Map<Parameter, Parameter> oldToNewParameters = ContainerUtil.newLinkedHashMap();
|
||||
for (KotlinParameterTablePanel.ParameterInfo parameterInfo : newParameterInfos) {
|
||||
@@ -283,7 +322,8 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
newReceiver,
|
||||
originalDescriptor.getTypeParameters(),
|
||||
replacementMap,
|
||||
controlFlow
|
||||
controlFlow,
|
||||
returnType != null ? returnType : originalDescriptor.getReturnType()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-2
@@ -32,6 +32,9 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputVa
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import kotlin.properties.Delegates
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
|
||||
import org.jetbrains.kotlin.idea.util.isAnnotatedNotNull
|
||||
import org.jetbrains.kotlin.idea.util.isAnnotatedNullable
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiRange
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.idea.util.isUnit
|
||||
@@ -291,6 +294,19 @@ data class ControlFlow(
|
||||
}
|
||||
}
|
||||
|
||||
val ControlFlow.possibleReturnTypes: List<JetType>
|
||||
get() {
|
||||
val returnType = outputValueBoxer.returnType
|
||||
return when {
|
||||
!returnType.isNullabilityFlexible() ->
|
||||
listOf(returnType)
|
||||
returnType.isAnnotatedNotNull(), returnType.isAnnotatedNullable() ->
|
||||
listOf(approximateFlexibleTypes(returnType))
|
||||
else ->
|
||||
returnType.getCapability(javaClass<Flexibility>()).let { listOf(it.upperBound, it.lowerBound) }
|
||||
}
|
||||
}
|
||||
|
||||
fun ControlFlow.toDefault(): ControlFlow =
|
||||
copy(outputValues = outputValues.filterNot { it is OutputValue.Jump || it is OutputValue.ExpressionValue })
|
||||
|
||||
@@ -303,7 +319,8 @@ data class ExtractableCodeDescriptor(
|
||||
val receiverParameter: Parameter?,
|
||||
val typeParameters: List<TypeParameter>,
|
||||
val replacementMap: Map<Int, Replacement>,
|
||||
val controlFlow: ControlFlow
|
||||
val controlFlow: ControlFlow,
|
||||
val returnType: JetType
|
||||
) {
|
||||
val name: String get() = suggestedNames.firstOrNull() ?: ""
|
||||
val duplicates: List<DuplicateInfo> by Delegates.lazy { findDuplicates() }
|
||||
@@ -365,7 +382,7 @@ enum class ExtractionTarget(val name: String) {
|
||||
|
||||
fun checkSignatureAndParent(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
if (!descriptor.parameters.isEmpty()) return false
|
||||
if (descriptor.controlFlow.outputValueBoxer.returnType.isUnit()) return false
|
||||
if (descriptor.returnType.isUnit()) return false
|
||||
|
||||
val parent = descriptor.extractionData.targetSibling.getParent()
|
||||
return (parent is JetFile || parent is JetClassBody)
|
||||
|
||||
+2
-1
@@ -885,7 +885,8 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
receiverParameter,
|
||||
paramsInfo.typeParameters.sortBy { it.originalDeclaration.getName()!! },
|
||||
paramsInfo.replacementMap,
|
||||
if (messages.isEmpty()) controlFlow else controlFlow.toDefault()
|
||||
if (messages.isEmpty()) controlFlow else controlFlow.toDefault(),
|
||||
returnType
|
||||
),
|
||||
if (messages.isEmpty()) Status.SUCCESS else Status.NON_CRITICAL_ERROR,
|
||||
messages
|
||||
|
||||
+5
-3
@@ -45,6 +45,8 @@ import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.types.isNullabilityFlexible
|
||||
import java.util.ArrayList
|
||||
import java.util.Collections
|
||||
import java.util.HashMap
|
||||
@@ -85,7 +87,7 @@ fun ExtractionGeneratorConfiguration.getDeclarationText(
|
||||
parameter.getParameterType(descriptor.extractionData.options.allowSpecialClassNames).typeAsString())
|
||||
}
|
||||
|
||||
with(descriptor.controlFlow.outputValueBoxer.returnType) {
|
||||
with(descriptor.returnType) {
|
||||
if (isDefault() || isError() || extractionTarget == ExtractionTarget.PROPERTY_WITH_INITIALIZER) {
|
||||
builder.noReturnType()
|
||||
} else {
|
||||
@@ -272,7 +274,7 @@ private fun makeCall(
|
||||
}
|
||||
else {
|
||||
val varNameValidator = JetNameValidatorImpl(block, anchorInBlock, JetNameValidatorImpl.Target.PROPERTIES)
|
||||
val resultVal = JetNameSuggester.suggestNames(controlFlow.outputValueBoxer.returnType, varNameValidator, null).first()
|
||||
val resultVal = JetNameSuggester.suggestNames(extractableDescriptor.returnType, varNameValidator, null).first()
|
||||
block.addBefore(psiFactory.createDeclaration("val $resultVal = $callText"), anchorInBlock)
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
controlFlow.outputValueBoxer.getUnboxingExpressions(resultVal)
|
||||
@@ -541,7 +543,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
val bodyExpression = body.getStatements().singleOrNull()
|
||||
val bodyOwner = body.getParent() as JetDeclarationWithBody
|
||||
if (bodyExpression != null && !bodyExpression.isMultiLine() && convertToExpressionBody.isApplicableTo(bodyOwner)) {
|
||||
convertToExpressionBody.applyTo(bodyOwner)
|
||||
convertToExpressionBody.applyTo(bodyOwner, !descriptor.returnType.isFlexible())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -75,7 +75,7 @@ public class KotlinIntroduceParameterDialog private (
|
||||
introduceParameterDescriptor,
|
||||
lambdaExtractionDescriptor,
|
||||
lambdaExtractionDescriptor.suggestedNames.copyToArray(),
|
||||
listOf(lambdaExtractionDescriptor.controlFlow.outputValueBoxer.returnType),
|
||||
listOf(lambdaExtractionDescriptor.returnType),
|
||||
helper
|
||||
)
|
||||
|
||||
@@ -275,7 +275,8 @@ public class KotlinIntroduceParameterDialog private (
|
||||
chosenName,
|
||||
"",
|
||||
parameterTablePanel?.getReceiverInfo(),
|
||||
parameterTablePanel?.getParameterInfos() ?: listOf()
|
||||
parameterTablePanel?.getParameterInfos() ?: listOf(),
|
||||
null
|
||||
)
|
||||
val options = ExtractionGeneratorOptions.DEFAULT.copy(
|
||||
target = ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION,
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ public class KotlinIntroducePropertyHandler(
|
||||
project = project,
|
||||
title = INTRODUCE_PROPERTY,
|
||||
doNotChangeVar = false,
|
||||
exprType = descriptor.controlFlow.outputValueBoxer.returnType,
|
||||
exprType = descriptor.returnType,
|
||||
extractionResult = it,
|
||||
availableTargets = propertyTargets.filter { it.isAvailable(descriptor) }
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// SUGGESTED_RETURN_TYPES: kotlin.Boolean?, kotlin.Boolean
|
||||
// PARAM_DESCRIPTOR: value-parameter val it: kotlin.Map.Entry<(Boolean..Boolean?), (Boolean..Boolean?)> defined in test.<anonymous>
|
||||
// PARAM_TYPES: kotlin.Map.Entry<(Boolean..Boolean?), (Boolean..Boolean?)>
|
||||
fun test() {
|
||||
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
// SUGGESTED_RETURN_TYPES: kotlin.Boolean?, kotlin.Boolean
|
||||
// PARAM_DESCRIPTOR: value-parameter val it: kotlin.Map.Entry<(Boolean..Boolean?), (Boolean..Boolean?)> defined in test.<anonymous>
|
||||
// PARAM_TYPES: kotlin.Map.Entry<(Boolean..Boolean?), (Boolean..Boolean?)>
|
||||
fun test() {
|
||||
J.getMap().filter { b(it) }
|
||||
}
|
||||
|
||||
private fun b(it: Map.Entry<Boolean, Boolean>) = it.getKey()
|
||||
private fun b(it: Map.Entry<Boolean, Boolean>): Boolean? = it.getKey()
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class J {
|
||||
@NotNull
|
||||
static String notNull() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// SUGGESTED_NAMES: s, getS
|
||||
fun test() {
|
||||
val s = <selection>J.notNull()</selection>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
// SUGGESTED_NAMES: s, getS
|
||||
fun test() {
|
||||
val s = s()
|
||||
}
|
||||
|
||||
private fun s(): String = J.notNull()
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class J {
|
||||
@Nullable
|
||||
static String nullable() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// SUGGESTED_NAMES: s, getS
|
||||
fun test() {
|
||||
val s = <selection>J.nullable()</selection>
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// SUGGESTED_NAMES: s, getS
|
||||
fun test() {
|
||||
val s = s()
|
||||
}
|
||||
|
||||
private fun s(): String? = J.nullable()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class J {
|
||||
static String unknown() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SUGGESTED_NAMES: s, getS
|
||||
// SUGGESTED_RETURN_TYPES: kotlin.String?, kotlin.String
|
||||
fun test() {
|
||||
val s = <selection>J.unknown()</selection>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// SUGGESTED_NAMES: s, getS
|
||||
// SUGGESTED_RETURN_TYPES: kotlin.String?, kotlin.String
|
||||
fun test() {
|
||||
val s = s()
|
||||
}
|
||||
|
||||
private fun s(): String? = J.unknown()
|
||||
+1
-1
@@ -15,4 +15,4 @@ class Foo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Foo<T>.t(l: String) = map[l]
|
||||
private fun <T> Foo<T>.t(l: String): T = map[l]
|
||||
+1
-1
@@ -11,5 +11,5 @@ class Foo<T> {
|
||||
return t(l)
|
||||
}
|
||||
|
||||
private fun t(l: String) = map[l]
|
||||
private fun t(l: String): T = map[l]
|
||||
}
|
||||
+8
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinI
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
@@ -239,6 +240,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
val explicitPreviousSibling = file.findElementByComment("// SIBLING:")
|
||||
val fileText = file.getText() ?: ""
|
||||
val expectedNames = InTextDirectivesUtils.findListWithPrefixes(fileText, "// SUGGESTED_NAMES: ")
|
||||
val expectedReturnTypes = InTextDirectivesUtils.findListWithPrefixes(fileText, "// SUGGESTED_RETURN_TYPES: ")
|
||||
val expectedDescriptors =
|
||||
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PARAM_DESCRIPTOR: ").joinToString()
|
||||
val expectedTypes =
|
||||
@@ -269,6 +271,9 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
) {
|
||||
val descriptor = descriptorWithConflicts.descriptor
|
||||
val actualNames = descriptor.suggestedNames
|
||||
val actualReturnTypes = descriptor.controlFlow.possibleReturnTypes.map {
|
||||
IdeDescriptorRenderers.SOURCE_CODE.renderType(it)
|
||||
}
|
||||
val allParameters = emptyOrSingletonList(descriptor.receiverParameter) + descriptor.parameters
|
||||
val actualDescriptors = allParameters.map { renderer.render(it.originalDescriptor) }.joinToString()
|
||||
val actualTypes = allParameters.map {
|
||||
@@ -278,6 +283,9 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
||||
if (actualNames.size() != 1 || expectedNames.isNotEmpty()) {
|
||||
assertEquals(expectedNames, actualNames, "Expected names mismatch.")
|
||||
}
|
||||
if (actualReturnTypes.size() != 1 || expectedReturnTypes.isNotEmpty()) {
|
||||
assertEquals(expectedReturnTypes, actualReturnTypes, "Expected return types mismatch.")
|
||||
}
|
||||
assertEquals(expectedDescriptors, actualDescriptors, "Expected descriptors mismatch.")
|
||||
assertEquals(expectedTypes, actualTypes, "Expected types mismatch.")
|
||||
|
||||
|
||||
+27
@@ -1050,6 +1050,33 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/controlFlow/returnTypeCandidates")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ReturnTypeCandidates extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInReturnTypeCandidates() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractFunction/controlFlow/returnTypeCandidates"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("javaAnnotatedNotNull.kt")
|
||||
public void testJavaAnnotatedNotNull() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/returnTypeCandidates/javaAnnotatedNotNull.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("javaAnnotatedNullable.kt")
|
||||
public void testJavaAnnotatedNullable() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/returnTypeCandidates/javaAnnotatedNullable.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("javaNoNullabilityAnnotation.kt")
|
||||
public void testJavaNoNullabilityAnnotation() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/returnTypeCandidates/javaNoNullabilityAnnotation.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/controlFlow/throws")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user