mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
extract method: suggest return type by unique variable defined in extracted code (IDEA-108296)
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class X {
|
||||
void foo() {
|
||||
<selection>int x = 0;</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class X {
|
||||
void foo() {
|
||||
int x = newMethod();
|
||||
}
|
||||
|
||||
private int newMethod() {
|
||||
int x = 0;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class X {
|
||||
void foo() {
|
||||
<selection>int x = 0;
|
||||
int y = 42;
|
||||
</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class X {
|
||||
void foo() {
|
||||
newMethod();
|
||||
|
||||
}
|
||||
|
||||
private int newMethod() {
|
||||
int x = 0;
|
||||
int y = 42;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user