extract method: suggest return type by unique variable defined in extracted code (IDEA-108296)

This commit is contained in:
Anna Kozlova
2014-12-04 10:51:10 +01:00
parent cab9d9a5e3
commit e13047d703
9 changed files with 103 additions and 6 deletions

View File

@@ -28,6 +28,7 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.IntArrayList;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -142,10 +143,12 @@ public class ControlFlowWrapper {
public static class ExitStatementsNotSameException extends Exception {}
@NotNull
public PsiVariable[] getOutputVariables() {
return getOutputVariables(myGenerateConditionalExit);
}
@NotNull
public PsiVariable[] getOutputVariables(boolean collectVariablesAtExitPoints) {
PsiVariable[] myOutputVariables = ControlFlowUtil.getOutputVariables(myControlFlow, myFlowStart, myFlowEnd, myExitPoints.toArray());
if (collectVariablesAtExitPoints) {

View File

@@ -41,6 +41,7 @@ import com.intellij.ui.EditorTextField;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.NonFocusableCheckBox;
import com.intellij.ui.SeparatorFactory;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MultiMap;
@@ -240,14 +241,28 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac
return main;
}
protected boolean isVoidReturn() {
return false;
}
@Nullable
private JPanel createReturnTypePanel() {
if (TypeConversionUtil.isPrimitiveWrapper(myReturnType) && myNullness == Nullness.NULLABLE) {
return null;
}
mySelector = new TypeSelectorManagerImpl(myProject, myReturnType, findOccurrences(), areTypesDirected()).getTypeSelector();
final TypeSelectorManagerImpl manager = new TypeSelectorManagerImpl(myProject, myReturnType, findOccurrences(), areTypesDirected()) {
@Override
public PsiType[] getTypesForAll(boolean direct) {
final PsiType[] types = super.getTypesForAll(direct);
return !isVoidReturn() ? types : ArrayUtil.prepend(PsiType.VOID, types);
}
};
mySelector = manager.getTypeSelector();
final JComponent component = mySelector.getComponent();
if (component instanceof JComboBox) {
if (isVoidReturn()) {
mySelector.selectType(PsiType.VOID);
}
final JPanel returnTypePanel = new JPanel(new BorderLayout(2, 0));
final JLabel label = new JLabel(RefactoringBundle.message("changeSignature.return.type.prompt"));
returnTypePanel.add(label, BorderLayout.NORTH);

View File

@@ -53,6 +53,8 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.codeStyle.JavaCodeStyleManagerImpl;
import com.intellij.psi.scope.processor.VariablesProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.PsiElementProcessor;
@@ -112,6 +114,7 @@ public class ExtractMethodProcessor implements MatchProvider {
protected InputVariables myInputVariables; // input variables
protected PsiVariable[] myOutputVariables; // output variables
protected PsiVariable myOutputVariable; // the only output variable
private PsiVariable myArtificialOutputVariable;
private Collection<PsiStatement> myExitStatements;
private boolean myHasReturnStatement; // there is a return statement
@@ -339,6 +342,23 @@ public class ExtractMethodProcessor implements MatchProvider {
: null;
}
@Nullable
private PsiVariable getArtificialOutputVariable() {
if (myOutputVariables.length == 0) {
final VariablesProcessor processor = new VariablesProcessor(true) {
@Override
protected boolean check(PsiVariable var, ResolveState state) {
return isDeclaredInside(var);
}
};
PsiScopesUtil.treeWalkUp(processor, myElements[myElements.length - 1], myCodeFragmentMember);
if (processor.size() == 1) {
return processor.getResult(0);
}
}
return null;
}
private boolean areAllExitPointsAreNotNull(PsiType returnStatementType) {
if (insertNotNullCheckIfPossible() && myControlFlowWrapper.getOutputVariables(false).length == 0) {
boolean isNotNull = returnStatementType != null && returnStatementType != PsiType.VOID;
@@ -508,7 +528,9 @@ public class ExtractMethodProcessor implements MatchProvider {
final List<VariableData> variables = myInputVariables.getInputVariables();
myVariableDatum = variables.toArray(new VariableData[variables.size()]);
myNullness = initNullness();
return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, myReturnType, getTypeParameterList(),
myArtificialOutputVariable = PsiType.VOID.equals(myReturnType) ? getArtificialOutputVariable() : null;
final PsiType returnType = myArtificialOutputVariable != null ? myArtificialOutputVariable.getType() : myReturnType;
return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, returnType, getTypeParameterList(),
getThrownExceptions(), isStatic(), isCanBeStatic(), myCanBeChainedConstructor,
suggestInitialMethodName(),
myRefactoringName, myHelpId, myNullness, myElements) {
@@ -526,6 +548,10 @@ public class ExtractMethodProcessor implements MatchProvider {
return ExtractMethodProcessor.this.isOutputVariable(var);
}
protected boolean isVoidReturn() {
return myArtificialOutputVariable != null;
}
@Override
protected void checkMethodConflicts(MultiMap<PsiElement, String> conflicts) {
super.checkMethodConflicts(conflicts);
@@ -669,6 +695,9 @@ public class ExtractMethodProcessor implements MatchProvider {
myInputVariables.setPassFields(true);
myStatic = true;
}
if (PsiType.VOID.equals(myReturnType)) {
myArtificialOutputVariable = getArtificialOutputVariable();
}
testPrepare();
if (returnType != null) {
myReturnType = returnType;
@@ -850,9 +879,14 @@ public class ExtractMethodProcessor implements MatchProvider {
myMethodCall = (PsiMethodCallExpression)((PsiReturnStatement)statement).getReturnValue().replace(myMethodCall);
}
else {
PsiStatement statement = myElementFactory.createStatementFromText("x();", null);
statement = (PsiStatement)addToMethodCallLocation(statement);
myMethodCall = (PsiMethodCallExpression)((PsiExpressionStatement)statement).getExpression().replace(myMethodCall);
if (myArtificialOutputVariable != null && myReturnType != PsiType.VOID) {
declareVariableAtMethodCallLocation(myArtificialOutputVariable.getName());
}
else {
PsiStatement statement = myElementFactory.createStatementFromText("x();", null);
statement = (PsiStatement)addToMethodCallLocation(statement);
myMethodCall = (PsiMethodCallExpression)((PsiExpressionStatement)statement).getExpression().replace(myMethodCall);
}
}
if (myHasReturnStatement && !myHasReturnStatementOutput && !hasNormalExit()) {
PsiStatement statement = myElementFactory.createStatementFromText("return;", null);
@@ -1012,6 +1046,9 @@ public class ExtractMethodProcessor implements MatchProvider {
}
}
}
else if (myArtificialOutputVariable != null && !PsiType.VOID.equals(myReturnType)) {
body.add(myElementFactory.createStatementFromText("return " + myArtificialOutputVariable.getName() + ";", null));
}
return exitStatementCopy;
}

View File

@@ -205,7 +205,7 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager {
}
}
private PsiType[] getTypesForAll(final boolean areTypesDirected) {
protected PsiType[] getTypesForAll(final boolean areTypesDirected) {
final ArrayList<ExpectedTypeInfo[]> expectedTypesFromAll = new ArrayList<ExpectedTypeInfo[]>();
for (PsiExpression occurrence : myOccurrences) {
final ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(occurrence, false, myOccurrenceClassProvider, isUsedAfter());

View File

@@ -0,0 +1,5 @@
class X {
void foo() {
<selection>int x = 0;</selection>
}
}

View File

@@ -0,0 +1,10 @@
class X {
void foo() {
int x = newMethod();
}
private int newMethod() {
int x = 0;
return x;
}
}

View File

@@ -0,0 +1,7 @@
class X {
void foo() {
<selection>int x = 0;
int y = 42;
</selection>
}
}

View File

@@ -0,0 +1,11 @@
class X {
void foo() {
newMethod();
}
private int newMethod() {
int x = 0;
int y = 42;
}
}

View File

@@ -629,6 +629,15 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
doTestReturnTypeChanged(PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())));
}
public void testMakeVoidMethodReturnVariable() throws Exception {
doTestReturnTypeChanged(PsiType.INT);
}
public void testMultipleVarsInMethodNoReturnStatementAndAssignment() throws Exception {
//return type should not be suggested but still
doTestReturnTypeChanged(PsiType.INT);
}
public void testPassFieldAsParameterAndMakeStatic() throws Exception {
doTestPassFieldsAsParams();
}