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:
Alexey Sedunov
2015-05-25 14:42:55 +03:00
parent 72205540d6
commit 58ef7f2691
24 changed files with 221 additions and 37 deletions
@@ -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)
}
}
@@ -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="&amp;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">
@@ -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()
);
}
}
@@ -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)
@@ -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
@@ -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())
}
}
}
@@ -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,
@@ -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() {
@@ -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()
@@ -0,0 +1,8 @@
import org.jetbrains.annotations.NotNull;
public class J {
@NotNull
static String notNull() {
return "";
}
}
@@ -0,0 +1,5 @@
// WITH_RUNTIME
// SUGGESTED_NAMES: s, getS
fun test() {
val s = <selection>J.notNull()</selection>
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
// SUGGESTED_NAMES: s, getS
fun test() {
val s = s()
}
private fun s(): String = J.notNull()
@@ -0,0 +1,8 @@
import org.jetbrains.annotations.Nullable;
public class J {
@Nullable
static String nullable() {
return null;
}
}
@@ -0,0 +1,4 @@
// SUGGESTED_NAMES: s, getS
fun test() {
val s = <selection>J.nullable()</selection>
}
@@ -0,0 +1,6 @@
// SUGGESTED_NAMES: s, getS
fun test() {
val s = s()
}
private fun s(): String? = J.nullable()
@@ -0,0 +1,5 @@
public class J {
static String unknown() {
return "";
}
}
@@ -0,0 +1,5 @@
// SUGGESTED_NAMES: s, getS
// SUGGESTED_RETURN_TYPES: kotlin.String?, kotlin.String
fun test() {
val s = <selection>J.unknown()</selection>
}
@@ -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()
@@ -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]
@@ -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]
}
@@ -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.")
@@ -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)