mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-209056 Action to transform method with multiple returns into the method with single exit point
First implementation
This commit is contained in:
@@ -1277,6 +1277,10 @@
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitSwitchBranchWithSeveralCaseValuesAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.singlereturn.ConvertToSingleReturnAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.WrapWithUnmodifiableAction</className>
|
||||
<category>Java/Other</category>
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl.singlereturn;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class ConvertToSingleReturnAction extends PsiElementBaseIntentionAction {
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
|
||||
PsiCodeBlock block = findBlock(element);
|
||||
if (block == null) return;
|
||||
PsiType returnType = PsiTypesUtil.getMethodReturnType(block);
|
||||
if (returnType == null) return;
|
||||
PsiCodeBlock copy = (PsiCodeBlock)block.copy();
|
||||
|
||||
process(project, copy, returnType, FinishMarker.defineFinishMarker(block, returnType));
|
||||
CodeStyleManager.getInstance(project).reformat(block.replace(copy));
|
||||
}
|
||||
|
||||
private static void process(@NotNull Project project,
|
||||
PsiCodeBlock block,
|
||||
PsiType returnType,
|
||||
FinishMarker marker) {
|
||||
ExitContext exitContext = new ExitContext(block, returnType, marker);
|
||||
|
||||
while (true) {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiReturnStatement returnStatement = getNonTerminalReturn(block);
|
||||
if (returnStatement == null) break;
|
||||
ReturnReplacementContext.replaceSingleReturn(project, block, exitContext, returnStatement);
|
||||
}
|
||||
exitContext.declareVariables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
|
||||
PsiCodeBlock block = findBlock(element);
|
||||
if (block == null) return false;
|
||||
PsiType returnType = PsiTypesUtil.getMethodReturnType(block);
|
||||
return returnType != null && getNonTerminalReturn(block) != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiCodeBlock findBlock(PsiElement element) {
|
||||
PsiParameterListOwner owner = PsiTreeUtil.getParentOfType(element, PsiParameterListOwner.class, false, PsiCodeBlock.class);
|
||||
if (owner == null) return null;
|
||||
return tryCast(owner.getBody(), PsiCodeBlock.class);
|
||||
}
|
||||
|
||||
private static PsiReturnStatement getNonTerminalReturn(@NotNull PsiCodeBlock block) {
|
||||
PsiStatement lastStatement = ArrayUtil.getLastElement(block.getStatements());
|
||||
if (lastStatement == null) return null;
|
||||
class Visitor extends JavaRecursiveElementWalkingVisitor {
|
||||
private PsiReturnStatement myReturnStatement;
|
||||
|
||||
@Override
|
||||
public void visitReturnStatement(PsiReturnStatement statement) {
|
||||
super.visitReturnStatement(statement);
|
||||
if (lastStatement != statement) {
|
||||
myReturnStatement = statement;
|
||||
stopWalking();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpression(PsiExpression expression) {}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {}
|
||||
}
|
||||
Visitor visitor = new Visitor();
|
||||
block.accept(visitor);
|
||||
return visitor.myReturnStatement;
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return CodeInsightBundle.message("intention.convert.to.single.return.name");
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl.singlereturn;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.BoolUtils;
|
||||
import com.siyeh.ig.psiutils.EquivalenceChecker;
|
||||
import com.siyeh.ig.psiutils.VariableNameGenerator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Tracks method exit strategy and additional variables which could be necessary for single-return conversion
|
||||
*/
|
||||
class ExitContext {
|
||||
private final @NotNull PsiType myReturnType;
|
||||
private final @NotNull FinishMarker.FinishMarkerType myFinishMarkerType;
|
||||
private String myFinishedVariable;
|
||||
private final @NotNull PsiCodeBlock myBlock;
|
||||
private final @NotNull String myReturnVariable;
|
||||
private final @NotNull PsiElementFactory myFactory;
|
||||
boolean myReturnVariableUsed = false;
|
||||
PsiExpression myReturnVariableDefaultValue;
|
||||
|
||||
ExitContext(@NotNull PsiCodeBlock block, @NotNull PsiType returnType, @NotNull FinishMarker marker) {
|
||||
myBlock = block;
|
||||
myFactory = JavaPsiFacade.getElementFactory(block.getProject());
|
||||
myReturnType = returnType;
|
||||
myReturnVariable =
|
||||
new VariableNameGenerator(block, VariableKind.LOCAL_VARIABLE).byName("result", "res").byType(returnType).generate(false);
|
||||
myReturnVariableDefaultValue = marker.myDefaultValue;
|
||||
if (myReturnVariableDefaultValue != null && myReturnVariableDefaultValue.isPhysical()) {
|
||||
myReturnVariableDefaultValue = (PsiExpression)myReturnVariableDefaultValue.copy();
|
||||
}
|
||||
myFinishMarkerType = marker.myType;
|
||||
}
|
||||
|
||||
String generateExitCondition() {
|
||||
switch (myFinishMarkerType) {
|
||||
case BOOLEAN_FALSE:
|
||||
return "!" + myReturnVariable;
|
||||
case BOOLEAN_TRUE:
|
||||
return myReturnVariable;
|
||||
case VALUE_EQUAL:
|
||||
return myReturnVariable + "==" + myReturnVariableDefaultValue.getText();
|
||||
case VALUE_NON_EQUAL:
|
||||
return myReturnVariable + "!=" + myReturnVariableDefaultValue.getText();
|
||||
default:
|
||||
assert myFinishedVariable != null;
|
||||
return myFinishedVariable;
|
||||
}
|
||||
}
|
||||
|
||||
String getNonExitCondition() {
|
||||
switch (myFinishMarkerType) {
|
||||
case BOOLEAN_FALSE:
|
||||
return myReturnVariable;
|
||||
case BOOLEAN_TRUE:
|
||||
return "!" + myReturnVariable;
|
||||
case VALUE_EQUAL:
|
||||
return myReturnVariable + "!=" + myReturnVariableDefaultValue.getText();
|
||||
case VALUE_NON_EQUAL:
|
||||
return myReturnVariable + "==" + myReturnVariableDefaultValue.getText();
|
||||
default:
|
||||
assert myFinishedVariable != null;
|
||||
return "!" + myFinishedVariable;
|
||||
}
|
||||
}
|
||||
|
||||
void registerReturnValue(PsiExpression value, List<String> replacements) {
|
||||
myReturnVariableUsed = true;
|
||||
if (FinishMarker.canMoveToStart(value) &&
|
||||
(myReturnVariableDefaultValue == null ||
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(myReturnVariableDefaultValue, value))) {
|
||||
myReturnVariableDefaultValue = (PsiExpression)value.copy();
|
||||
}
|
||||
else {
|
||||
replacements.add(myReturnVariable + "=" + value.getText() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
void register(List<String> replacements) {
|
||||
if (myFinishMarkerType != FinishMarker.FinishMarkerType.SEPARATE_VAR) return;
|
||||
if (myFinishedVariable == null) {
|
||||
myFinishedVariable =
|
||||
new VariableNameGenerator(myBlock, VariableKind.LOCAL_VARIABLE).byName("finished", "completed").generate(false);
|
||||
}
|
||||
String firstItem = ContainerUtil.getFirstItem(replacements);
|
||||
String assignment = myFinishedVariable + "=true;";
|
||||
if (!assignment.equals(firstItem)) {
|
||||
replacements.add(0, assignment);
|
||||
}
|
||||
}
|
||||
|
||||
void declareVariables() {
|
||||
if (myFinishedVariable != null) {
|
||||
PsiJavaToken start = requireNonNull(myBlock.getLBrace());
|
||||
PsiExpression initializer = myFactory.createExpressionFromText("false", null);
|
||||
PsiDeclarationStatement declaration =
|
||||
myFactory.createVariableDeclarationStatement(myFinishedVariable, PsiType.BOOLEAN, initializer);
|
||||
myBlock.addAfter(declaration, start);
|
||||
}
|
||||
if (myReturnVariableUsed) {
|
||||
PsiJavaToken start = requireNonNull(myBlock.getLBrace());
|
||||
if (myReturnVariableDefaultValue == null && myFinishedVariable != null) {
|
||||
myReturnVariableDefaultValue = myFactory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(myReturnType), null);
|
||||
}
|
||||
PsiDeclarationStatement declaration =
|
||||
myFactory.createVariableDeclarationStatement(myReturnVariable, myReturnType, myReturnVariableDefaultValue);
|
||||
myBlock.addAfter(declaration, start);
|
||||
PsiJavaToken end = requireNonNull(myBlock.getRBrace());
|
||||
myBlock.addBefore(myFactory.createStatementFromText("return " + myReturnVariable + ";", myBlock), end);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFinishCondition(PsiStatement statement) {
|
||||
if (!(statement instanceof PsiIfStatement)) return false;
|
||||
PsiExpression condition = ((PsiIfStatement)statement).getCondition();
|
||||
if (condition == null) return false;
|
||||
if (BoolUtils.isNegation(condition)) {
|
||||
condition = BoolUtils.getNegated(condition);
|
||||
}
|
||||
if (condition instanceof PsiBinaryExpression) {
|
||||
condition = ((PsiBinaryExpression)condition).getLOperand();
|
||||
}
|
||||
if (!(condition instanceof PsiReferenceExpression)) return false;
|
||||
PsiReferenceExpression ref = (PsiReferenceExpression)condition;
|
||||
return ref.getQualifierExpression() == null &&
|
||||
(Objects.equals(myFinishedVariable, ref.getReferenceName()) ||
|
||||
Objects.equals(myReturnVariable, ref.getReferenceName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param statement statement to check
|
||||
* @return true if given statement is a return statement which returns registered default value, so it could be fully removed
|
||||
*/
|
||||
boolean isDefaultReturn(PsiStatement statement) {
|
||||
if (myReturnVariableDefaultValue != null && statement instanceof PsiReturnStatement) {
|
||||
PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence()
|
||||
.expressionsAreEquivalent(myReturnVariableDefaultValue, returnStatement.getReturnValue()) &&
|
||||
!FinishMarker.mayNeedMarker(returnStatement, myBlock);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl.singlereturn;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInspection.dataFlow.CommonDataflow;
|
||||
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.NULL;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Represents a way to indicate whether method execution is already finished
|
||||
*/
|
||||
class FinishMarker {
|
||||
/**
|
||||
* Type of finish marker
|
||||
*/
|
||||
final @NotNull FinishMarkerType myType;
|
||||
/**
|
||||
* Sentinel value
|
||||
*/
|
||||
final @Nullable PsiExpression myDefaultValue;
|
||||
|
||||
private FinishMarker(@NotNull FinishMarkerType type, @Nullable PsiExpression value) {
|
||||
myType = type;
|
||||
myDefaultValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param block method body (must be physical as CommonDataflow will be queried)
|
||||
* @param returnType method return type
|
||||
* @return a FinishMarker which is suitable for given method
|
||||
*/
|
||||
static FinishMarker defineFinishMarker(@NotNull PsiCodeBlock block, @NotNull PsiType returnType) {
|
||||
List<PsiReturnStatement> returns = findReturns(block);
|
||||
boolean mayNeedMarker = mayNeedMarker(returns, block);
|
||||
return defineFinishMarker(block, returns, returnType, mayNeedMarker, JavaPsiFacade.getElementFactory(block.getProject()));
|
||||
}
|
||||
|
||||
private static boolean mayNeedMarker(List<PsiReturnStatement> returns, PsiCodeBlock block) {
|
||||
for (PsiReturnStatement returnStatement : returns) {
|
||||
if (mayNeedMarker(returnStatement, block)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we may need a marker value to indicate premature exit from given return statement.
|
||||
*
|
||||
* @param returnStatement return statement to check
|
||||
* @param block method body (ancestor of return statement).
|
||||
* @return false if it's possible to transform the code removing given return statement without introducing a marker;
|
||||
* true if marker might be necessary.
|
||||
*/
|
||||
static boolean mayNeedMarker(PsiReturnStatement returnStatement, PsiCodeBlock block) {
|
||||
PsiElement parent = returnStatement.getParent();
|
||||
if (parent instanceof PsiCodeBlock) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (grandParent instanceof PsiStatement) {
|
||||
parent = grandParent.getParent();
|
||||
}
|
||||
else {
|
||||
return parent != block;
|
||||
}
|
||||
}
|
||||
if (!(parent instanceof PsiStatement)) return true;
|
||||
PsiStatement currentContext = (PsiStatement)parent;
|
||||
PsiStatement loopOrSwitch = PsiTreeUtil.getNonStrictParentOfType(currentContext, PsiLoopStatement.class, PsiSwitchStatement.class);
|
||||
if (loopOrSwitch != null && PsiTreeUtil.isAncestor(block, loopOrSwitch, true)) {
|
||||
currentContext = loopOrSwitch;
|
||||
}
|
||||
else {
|
||||
while (true) {
|
||||
PsiElement ifParent = currentContext.getParent();
|
||||
if (!(ifParent instanceof PsiCodeBlock)) break;
|
||||
if (!(ifParent.getParent() instanceof PsiStatement)) {
|
||||
return ifParent != block;
|
||||
}
|
||||
currentContext = (PsiStatement)ifParent.getParent();
|
||||
if (!(currentContext instanceof PsiBlockStatement) ||
|
||||
!(currentContext.getParent() instanceof PsiIfStatement) ||
|
||||
ControlFlowUtils.codeBlockMayCompleteNormally((PsiCodeBlock)ifParent)) {
|
||||
break;
|
||||
}
|
||||
currentContext = (PsiStatement)currentContext.getParent();
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
PsiElement contextParent = currentContext.getParent();
|
||||
if (contextParent instanceof PsiCodeBlock) {
|
||||
PsiStatement[] contextStatements = ((PsiCodeBlock)contextParent).getStatements();
|
||||
int pos = ArrayUtil.indexOf(contextStatements, currentContext);
|
||||
assert pos >= 0;
|
||||
if (pos < contextStatements.length - 1) return true;
|
||||
if (contextParent == block) return false;
|
||||
if (!(contextParent.getParent() instanceof PsiStatement)) return true;
|
||||
currentContext = (PsiStatement)contextParent.getParent();
|
||||
}
|
||||
else if (contextParent instanceof PsiIfStatement || contextParent instanceof PsiLabeledStatement) {
|
||||
currentContext = (PsiStatement)contextParent;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static FinishMarker defineFinishMarker(PsiCodeBlock block, List<PsiReturnStatement> returns, PsiType returnType,
|
||||
boolean mayNeedMarker, PsiElementFactory factory) {
|
||||
if (PsiType.VOID.equals(returnType)) {
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, null);
|
||||
}
|
||||
PsiReturnStatement terminalReturn = tryCast(ArrayUtil.getLastElement(block.getStatements()), PsiReturnStatement.class);
|
||||
List<PsiExpression> nonTerminalReturns = StreamEx.of(returns).without(terminalReturn)
|
||||
.map(PsiReturnStatement::getReturnValue)
|
||||
.map(PsiUtil::skipParenthesizedExprDown).toList();
|
||||
if (nonTerminalReturns.size() == 0) {
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, null);
|
||||
}
|
||||
Set<Object> nonTerminalReturnValues = StreamEx.of(nonTerminalReturns)
|
||||
.map(val -> val instanceof PsiLiteralExpression ? ((PsiLiteralExpression)val).getValue() : NULL)
|
||||
.toSet();
|
||||
if (!mayNeedMarker) {
|
||||
if (nonTerminalReturnValues.size() == 1 && nonTerminalReturnValues.iterator().next() != NULL) {
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, nonTerminalReturns.iterator().next());
|
||||
}
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, null);
|
||||
}
|
||||
if (PsiType.BOOLEAN.equals(returnType)) {
|
||||
if (nonTerminalReturnValues.size() == 1) {
|
||||
Object value = nonTerminalReturnValues.iterator().next();
|
||||
if (value instanceof Boolean) {
|
||||
boolean boolReturn = (boolean)value;
|
||||
FinishMarkerType markerType = boolReturn ? FinishMarkerType.BOOLEAN_TRUE : FinishMarkerType.BOOLEAN_FALSE;
|
||||
return new FinishMarker(markerType, factory.createExpressionFromText(String.valueOf(!boolReturn), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PsiType.INT.equals(returnType) || PsiType.LONG.equals(returnType)) {
|
||||
return getMarkerForIntegral(nonTerminalReturns, terminalReturn, returnType, factory);
|
||||
}
|
||||
if (!(returnType instanceof PsiPrimitiveType)) {
|
||||
if (StreamEx.of(nonTerminalReturns).map(ret -> NullabilityUtil.getExpressionNullability(ret, true))
|
||||
.allMatch(Nullability.NOT_NULL::equals)) {
|
||||
return new FinishMarker(FinishMarkerType.VALUE_NON_EQUAL, factory.createExpressionFromText("null", null));
|
||||
}
|
||||
}
|
||||
if (terminalReturn != null) {
|
||||
PsiExpression value = terminalReturn.getReturnValue();
|
||||
if (value != null && canMoveToStart(value)) {
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, value);
|
||||
}
|
||||
}
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static FinishMarker getMarkerForIntegral(List<PsiExpression> nonTerminalReturns,
|
||||
PsiReturnStatement terminalReturn,
|
||||
PsiType returnType, PsiElementFactory factory) {
|
||||
boolean isLong = PsiType.LONG.equals(returnType);
|
||||
LongRangeSet fullSet = requireNonNull(LongRangeSet.fromType(returnType));
|
||||
LongRangeSet set = nonTerminalReturns.stream()
|
||||
.map(CommonDataflow::getExpressionRange)
|
||||
.map(range -> range == null ? fullSet : range)
|
||||
.reduce(LongRangeSet::unite)
|
||||
.orElse(fullSet);
|
||||
if (!set.isEmpty() && !set.contains(fullSet)) {
|
||||
PsiExpression terminalReturnValue = terminalReturn == null ? null : terminalReturn.getReturnValue();
|
||||
LongRangeSet terminal = CommonDataflow.getExpressionRange(terminalReturnValue);
|
||||
Long point;
|
||||
if (terminal != null) {
|
||||
point = terminal.getConstantValue();
|
||||
if (point != null && !set.contains(point)) {
|
||||
PsiExpression defValue = canMoveToStart(terminalReturnValue) ?
|
||||
(PsiExpression)terminalReturnValue.copy() :
|
||||
factory.createExpressionFromText(point + (isLong ? "L" : ""), null);
|
||||
return new FinishMarker(FinishMarkerType.VALUE_NON_EQUAL, defValue);
|
||||
}
|
||||
}
|
||||
long[] candidates = {0, 1, -1, fullSet.min(), fullSet.max()};
|
||||
point = null;
|
||||
for (long candidate : candidates) {
|
||||
if (!set.contains(candidate)) {
|
||||
point = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (point != null) {
|
||||
String text = point == Integer.MIN_VALUE ? "java.lang.Integer.MIN_VALUE" :
|
||||
point == Integer.MAX_VALUE ? "java.lang.Integer.MAX_VALUE" :
|
||||
point == Long.MIN_VALUE ? "java.lang.Long.MIN_VALUE" :
|
||||
point == Long.MAX_VALUE ? "java.lang.Long.MAX_VALUE" :
|
||||
String.valueOf(point);
|
||||
return new FinishMarker(FinishMarkerType.VALUE_NON_EQUAL, factory.createExpressionFromText(text, null));
|
||||
}
|
||||
}
|
||||
return new FinishMarker(FinishMarkerType.SEPARATE_VAR, null);
|
||||
}
|
||||
|
||||
static boolean canMoveToStart(PsiExpression value) {
|
||||
if (!ExpressionUtils.isSafelyRecomputableExpression(value)) return false;
|
||||
PsiReferenceExpression ref = tryCast(PsiUtil.skipParenthesizedExprDown(value), PsiReferenceExpression.class);
|
||||
if (ref != null && !ref.isQualified()) {
|
||||
PsiVariable target = tryCast(ref.resolve(), PsiVariable.class);
|
||||
if (target instanceof PsiLocalVariable) return false;
|
||||
if (target instanceof PsiParameter) {
|
||||
PsiElement block = PsiUtil.getVariableCodeBlock(target, null);
|
||||
return block != null && HighlightControlFlowUtil.isEffectivelyFinal(target, block, null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<PsiReturnStatement> findReturns(PsiCodeBlock block) {
|
||||
List<PsiReturnStatement> result = new ArrayList<>();
|
||||
block.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitReturnStatement(PsiReturnStatement statement) {
|
||||
super.visitReturnStatement(statement);
|
||||
result.add(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpression(PsiExpression expression) {}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of finish marker
|
||||
*/
|
||||
enum FinishMarkerType {
|
||||
/**
|
||||
* If result boolean variable is true, then the method execution is finished
|
||||
*/
|
||||
BOOLEAN_TRUE,
|
||||
/**
|
||||
* If result boolean variable is false, then the method execution is finished
|
||||
*/
|
||||
BOOLEAN_FALSE,
|
||||
/**
|
||||
* If result variable is equal to sentinel, then the method execution is finished
|
||||
*/
|
||||
VALUE_EQUAL,
|
||||
/**
|
||||
* If result variable is not equal to sentinel, then the method execution is finished
|
||||
*/
|
||||
VALUE_NON_EQUAL,
|
||||
/**
|
||||
* Separate boolean variable is used to indicate whether the method execution is finished.
|
||||
* This value also used if it was detected that no finish marker is actually necessary
|
||||
*/
|
||||
SEPARATE_VAR
|
||||
}
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.intention.impl.singlereturn;
|
||||
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.openapi.diagnostic.Attachment;
|
||||
import com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.BoolUtils;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Performs replacement of single return statement as the part of {@link ConvertToSingleReturnAction}.
|
||||
*/
|
||||
class ReturnReplacementContext {
|
||||
private final Project myProject;
|
||||
private final PsiElementFactory myFactory;
|
||||
private final PsiCodeBlock myBlock;
|
||||
private final ExitContext myExitContext;
|
||||
private PsiReturnStatement myReturnStatement;
|
||||
private final List<String> myReplacements = new ArrayList<>();
|
||||
|
||||
private ReturnReplacementContext(Project project,
|
||||
PsiCodeBlock block,
|
||||
ExitContext context,
|
||||
PsiReturnStatement statement) {
|
||||
myProject = project;
|
||||
myFactory = JavaPsiFacade.getElementFactory(project);
|
||||
myBlock = block;
|
||||
myExitContext = context;
|
||||
myReturnStatement = statement;
|
||||
}
|
||||
|
||||
private void process() {
|
||||
PsiExpression value = myReturnStatement.getReturnValue();
|
||||
if (value != null) {
|
||||
myExitContext.registerReturnValue(value, myReplacements);
|
||||
}
|
||||
PsiStatement currentContext = goUp();
|
||||
while (currentContext != null) {
|
||||
currentContext = advance(currentContext);
|
||||
}
|
||||
replace();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiStatement goUp() {
|
||||
PsiElement parent = myReturnStatement.getParent();
|
||||
if (parent instanceof PsiCodeBlock) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (!(grandParent instanceof PsiSwitchStatement)) {
|
||||
PsiStatement[] statements = ((PsiCodeBlock)parent).getStatements();
|
||||
boolean afterReturn = false;
|
||||
for (PsiStatement statement : statements) {
|
||||
if (statement == myReturnStatement) {
|
||||
afterReturn = true;
|
||||
}
|
||||
else if (afterReturn) {
|
||||
// Unreachable statements after return (compilation error): remove them
|
||||
new CommentTracker().deleteAndRestoreComments(statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (grandParent instanceof PsiBlockStatement || grandParent instanceof PsiTryStatement ||
|
||||
grandParent instanceof PsiSwitchStatement) {
|
||||
parent = grandParent.getParent();
|
||||
}
|
||||
else if (parent != myBlock) {
|
||||
throw new RuntimeExceptionWithAttachments("Unexpected structure: " + grandParent.getClass(),
|
||||
new Attachment("body.txt", myBlock.getText()),
|
||||
new Attachment("context.txt", grandParent.getText()));
|
||||
}
|
||||
}
|
||||
if (!(parent instanceof PsiStatement)) {
|
||||
throw new RuntimeExceptionWithAttachments("Unexpected structure: " + parent.getClass(),
|
||||
new Attachment("body.txt", myBlock.getText()),
|
||||
new Attachment("context.txt", parent.getText()));
|
||||
}
|
||||
PsiStatement currentContext = (PsiStatement)parent;
|
||||
PsiStatement loopOrSwitch = PsiTreeUtil.getNonStrictParentOfType(currentContext, PsiLoopStatement.class, PsiSwitchStatement.class);
|
||||
if (loopOrSwitch != null && PsiTreeUtil.isAncestor(myBlock, loopOrSwitch, true)) {
|
||||
myReplacements.add("break;");
|
||||
return loopOrSwitch;
|
||||
}
|
||||
while (true) {
|
||||
if (currentContext instanceof PsiIfStatement) {
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)currentContext;
|
||||
boolean inThen = PsiTreeUtil.isAncestor(ifStatement.getThenBranch(), myReturnStatement, false);
|
||||
PsiElement ifParent = currentContext.getParent();
|
||||
if (ifParent instanceof PsiCodeBlock) {
|
||||
PsiCodeBlock resultBlock = swallowTail(currentContext, ifStatement, inThen, (PsiCodeBlock)ifParent);
|
||||
if (resultBlock != null &&
|
||||
!ControlFlowUtils.codeBlockMayCompleteNormally(resultBlock) &&
|
||||
ifParent.getParent() instanceof PsiBlockStatement &&
|
||||
ifParent.getParent().getParent() instanceof PsiIfStatement) {
|
||||
currentContext = (PsiStatement)ifParent.getParent().getParent();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentContext;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiStatement advance(PsiStatement currentContext) {
|
||||
PsiElement contextParent = currentContext.getParent();
|
||||
if (contextParent instanceof PsiLoopStatement) {
|
||||
Object mark = new Object();
|
||||
PsiTreeUtil.mark(myReturnStatement, mark);
|
||||
currentContext = BlockUtils.expandSingleStatementToBlockStatement(currentContext);
|
||||
contextParent = currentContext.getParent();
|
||||
myReturnStatement = (PsiReturnStatement)requireNonNull(PsiTreeUtil.releaseMark(currentContext, mark));
|
||||
}
|
||||
if (contextParent instanceof PsiCodeBlock) {
|
||||
PsiElement[] tail = extractTail(currentContext, (PsiCodeBlock)contextParent);
|
||||
PsiStatement loopOrSwitch = PsiTreeUtil.getParentOfType(currentContext, PsiLoopStatement.class, PsiSwitchStatement.class);
|
||||
if (loopOrSwitch != null && PsiTreeUtil.isAncestor(myBlock, loopOrSwitch, true)) {
|
||||
myExitContext.register(myReplacements);
|
||||
String exitStatement = "if(" + myExitContext.generateExitCondition() + ") break;";
|
||||
contextParent.addAfter(myFactory.createStatementFromText(exitStatement, currentContext), currentContext);
|
||||
currentContext = loopOrSwitch;
|
||||
return currentContext;
|
||||
}
|
||||
List<PsiStatement> statements = StreamEx.of(tail).select(PsiStatement.class).toList();
|
||||
if (!statements.isEmpty()) {
|
||||
PsiStatement statement = statements.get(0);
|
||||
if (statements.size() == 1 && myExitContext.isDefaultReturn(statement)) {
|
||||
new CommentTracker().deleteAndRestoreComments(statement);
|
||||
}
|
||||
else {
|
||||
myExitContext.register(myReplacements);
|
||||
if (!myExitContext.isFinishCondition(statement)) {
|
||||
String conditionalBlock = "if(" + myExitContext.getNonExitCondition() + ") {}";
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)myFactory.createStatementFromText(conditionalBlock, currentContext);
|
||||
PsiCodeBlock ifBlock = requireNonNull(((PsiBlockStatement)requireNonNull(ifStatement.getThenBranch())).getCodeBlock());
|
||||
PsiJavaToken lBrace = requireNonNull(ifBlock.getLBrace());
|
||||
PsiElement tailStart = ArrayUtil.getFirstElement(tail);
|
||||
PsiElement tailEnd = ArrayUtil.getLastElement(tail);
|
||||
ifBlock.addRangeAfter(tailStart, tailEnd, lBrace);
|
||||
contextParent.deleteChildRange(tailStart, tailEnd);
|
||||
contextParent.addAfter(ifStatement, currentContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contextParent == myBlock) {
|
||||
currentContext = null;
|
||||
}
|
||||
else if (contextParent.getParent() instanceof PsiStatement) {
|
||||
currentContext = (PsiStatement)contextParent.getParent();
|
||||
}
|
||||
else {
|
||||
throw new RuntimeExceptionWithAttachments("Unexpected structure: " + contextParent.getParent().getClass(),
|
||||
new Attachment("body.txt", myBlock.getText()),
|
||||
new Attachment("context.txt", contextParent.getText()));
|
||||
}
|
||||
}
|
||||
else if (contextParent instanceof PsiIfStatement || contextParent instanceof PsiLabeledStatement) {
|
||||
currentContext = (PsiStatement)contextParent;
|
||||
}
|
||||
else {
|
||||
throw new RuntimeExceptionWithAttachments("Unexpected structure: " + contextParent.getClass(),
|
||||
new Attachment("body.txt", myBlock.getText()),
|
||||
new Attachment("context.txt", contextParent.getText()));
|
||||
}
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement[] extractTail(PsiStatement current, PsiCodeBlock block) {
|
||||
PsiElement[] children = block.getChildren();
|
||||
int pos = ArrayUtil.indexOf(children, current);
|
||||
assert pos >= 0;
|
||||
PsiElement rBrace = block.getRBrace();
|
||||
int endPos = rBrace == null ? children.length : ArrayUtil.lastIndexOf(children, rBrace);
|
||||
assert endPos >= pos;
|
||||
return Arrays.copyOfRange(children, pos + 1, endPos);
|
||||
}
|
||||
|
||||
private PsiCodeBlock swallowTail(PsiStatement currentContext,
|
||||
PsiIfStatement ifStatement,
|
||||
boolean inThen, PsiCodeBlock ifParent) {
|
||||
PsiElement[] tail = extractTail(currentContext, ifParent);
|
||||
if (Arrays.stream(tail).noneMatch(PsiStatement.class::isInstance)) return null;
|
||||
PsiBlockStatement blockForTail = getBlockFromIf(ifStatement, inThen);
|
||||
PsiCodeBlock codeBlock = blockForTail.getCodeBlock();
|
||||
PsiJavaToken brace = requireNonNull(codeBlock.getRBrace());
|
||||
for (PsiElement element : tail) {
|
||||
if (element.isValid()) {
|
||||
codeBlock.addBefore(element, brace);
|
||||
element.delete();
|
||||
}
|
||||
}
|
||||
return codeBlock;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiBlockStatement getBlockFromIf(PsiIfStatement ifStatement, boolean inThen) {
|
||||
if (inThen) {
|
||||
PsiStatement elseBranch = ifStatement.getElseBranch();
|
||||
if (elseBranch == null) {
|
||||
ifStatement.setElseBranch(BlockUtils.createBlockStatement(myProject));
|
||||
return (PsiBlockStatement)ifStatement.getElseBranch();
|
||||
}
|
||||
if (!(elseBranch instanceof PsiBlockStatement)) {
|
||||
return (PsiBlockStatement)BlockUtils.expandSingleStatementToBlockStatement(elseBranch).getParent().getParent();
|
||||
}
|
||||
return (PsiBlockStatement)elseBranch;
|
||||
}
|
||||
else {
|
||||
PsiStatement thenBranch = ifStatement.getThenBranch();
|
||||
if (thenBranch == null) {
|
||||
ifStatement.setThenBranch(BlockUtils.createBlockStatement(myProject));
|
||||
return (PsiBlockStatement)ifStatement.getThenBranch();
|
||||
}
|
||||
if (!(thenBranch instanceof PsiBlockStatement)) {
|
||||
return (PsiBlockStatement)BlockUtils.expandSingleStatementToBlockStatement(thenBranch).getParent().getParent();
|
||||
}
|
||||
return (PsiBlockStatement)thenBranch;
|
||||
}
|
||||
}
|
||||
|
||||
private void replace() {
|
||||
if (!(myReturnStatement.getParent().getParent() instanceof PsiBlockStatement)) {
|
||||
myReturnStatement = BlockUtils.expandSingleStatementToBlockStatement(myReturnStatement);
|
||||
}
|
||||
PsiStatement[] newStatements = ContainerUtil.map2Array(
|
||||
myReplacements, PsiStatement.class, text -> myFactory.createStatementFromText(text, null));
|
||||
if (newStatements.length > 0) {
|
||||
BlockUtils.addBefore(myReturnStatement, newStatements);
|
||||
}
|
||||
PsiCodeBlock block = tryCast(myReturnStatement.getParent(), PsiCodeBlock.class);
|
||||
new CommentTracker().deleteAndRestoreComments(myReturnStatement);
|
||||
cleanUpEmptyBlocks(block);
|
||||
}
|
||||
|
||||
private static void cleanUpEmptyBlocks(PsiCodeBlock block) {
|
||||
if (block == null || !block.isEmpty()) return;
|
||||
PsiBlockStatement blockStatement = tryCast(block.getParent(), PsiBlockStatement.class);
|
||||
if (blockStatement == null) return;
|
||||
PsiIfStatement parent = tryCast(blockStatement.getParent(), PsiIfStatement.class);
|
||||
if (parent == null) return;
|
||||
PsiExpression condition = parent.getCondition();
|
||||
if (condition == null) return;
|
||||
if (blockStatement == parent.getElseBranch()) {
|
||||
new CommentTracker().deleteAndRestoreComments(blockStatement);
|
||||
}
|
||||
else if (blockStatement == parent.getThenBranch()) {
|
||||
if (parent.getElseBranch() != null) {
|
||||
new CommentTracker().replaceAndRestoreComments(blockStatement, parent.getElseBranch());
|
||||
parent.getElseBranch().delete();
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String negatedCondition = BoolUtils.getNegatedExpressionText(condition, ct);
|
||||
ct.replaceAndRestoreComments(condition, negatedCondition);
|
||||
}
|
||||
else if (!SideEffectChecker.mayHaveSideEffects(condition)) {
|
||||
PsiCodeBlock parentBlock = tryCast(parent.getParent(), PsiCodeBlock.class);
|
||||
new CommentTracker().deleteAndRestoreComments(parent);
|
||||
cleanUpEmptyBlocks(parentBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void replaceSingleReturn(@NotNull Project project,
|
||||
PsiCodeBlock block,
|
||||
ExitContext exitContext,
|
||||
PsiReturnStatement returnStatement) {
|
||||
new ReturnReplacementContext(project, block, exitContext, returnStatement).process();
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
void test(String s) {
|
||||
if(s != null) {
|
||||
if(!s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
void test(String s) {
|
||||
if(s == null) return;
|
||||
if(s.isEmpty()) return;
|
||||
System.out.println(s);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>This intention converts the method body to single 'return' form.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean noEmptyStrings(String[][] list) {
|
||||
boolean result = true;
|
||||
for (String[] inner : list) {
|
||||
for (String s : inner) {
|
||||
if (s.isEmpty()) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean hasEmptyString(List<String> list) {
|
||||
boolean result = false;
|
||||
for (String s : list) {
|
||||
if (s.isEmpty()) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean test(String[] arr) {
|
||||
boolean result = false;
|
||||
boolean finished = false;
|
||||
if (arr != null) {
|
||||
System.out.println("ok");
|
||||
for (String s : arr) {
|
||||
if (s.isEmpty()) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
if (!finished) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean test(String[] arr) {
|
||||
boolean result = true;
|
||||
if (arr == null) {
|
||||
result = false;
|
||||
} else {
|
||||
String s = arr[0];
|
||||
if (s == null) {
|
||||
result = false;
|
||||
} else {
|
||||
s = arr[1];
|
||||
if (s == null) {
|
||||
result = false;
|
||||
} else {
|
||||
if (arr.length > 3) {
|
||||
s = arr[2];
|
||||
if (s != null && s.isEmpty()) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean test(String s) {
|
||||
boolean result = false;
|
||||
if (s != null) {
|
||||
if (!s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String test2(List<String> list, String foo, String bar) {
|
||||
String result = foo;
|
||||
boolean finished = false;
|
||||
for (String s : list) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
bar = s;
|
||||
if (s.length() == i) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (finished) break;
|
||||
}
|
||||
if (!finished) {
|
||||
result = bar;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
native String get(String s);
|
||||
|
||||
String test(String[] data) {
|
||||
String result = null;
|
||||
boolean finished = false;
|
||||
if (data == null) {
|
||||
result = get("foo");
|
||||
} else {
|
||||
String s = data[0];
|
||||
int i = 0;
|
||||
if (data.length > 2) {
|
||||
if (data[2] != null) {
|
||||
if (data[2].isEmpty()) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
if (!finished) {
|
||||
while (true) {
|
||||
if (!s.isEmpty()) {
|
||||
if (s.length() > 2) {
|
||||
result = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.out.println(s);
|
||||
s = data[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int test(String s) {
|
||||
int result = 2;
|
||||
if (s == null) {
|
||||
if (!(Math.random() > 0.5)) {
|
||||
result = 4;
|
||||
}
|
||||
} else {
|
||||
if (s.isEmpty()) {
|
||||
result = 3;
|
||||
} else {
|
||||
System.out.println(s);
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int test(String s) {
|
||||
int result = 1;
|
||||
if (s == null) {
|
||||
if (Math.random() > 0.5) {
|
||||
result = 2;
|
||||
} else {
|
||||
System.out.println("going further");
|
||||
}
|
||||
}
|
||||
if (result == 1) {
|
||||
if (s.isEmpty()) {
|
||||
result = 3;
|
||||
} else {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int test(String[] strings) {
|
||||
int result = 0;
|
||||
for (String string : strings) {
|
||||
if (!string.isEmpty()) {
|
||||
result = string.length();
|
||||
break;// positive number
|
||||
}
|
||||
}
|
||||
if (result == 0) {
|
||||
result = strings.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int test(String[] strings) {
|
||||
int result = -1;
|
||||
for (String string : strings) {
|
||||
if (!string.equal("foo")) {
|
||||
result = string.length();
|
||||
break;// non-negative number
|
||||
}
|
||||
}
|
||||
if (result == -1) {
|
||||
result = strings.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String test(String[] strings) {
|
||||
String result = "";
|
||||
if (strings.length > 2) {
|
||||
String string = strings[0];
|
||||
if (string.equals(strings[1])) {
|
||||
result = foo(string);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String test(String[] strings) {
|
||||
String res = null;
|
||||
boolean finished = false;
|
||||
if (strings.length > 2) {
|
||||
String string = strings[0];
|
||||
if (string.equals(strings[1])) {
|
||||
finished = true;
|
||||
res = foo(string);
|
||||
}
|
||||
}
|
||||
if (!finished) {
|
||||
String result = bar();
|
||||
res = result;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String test(String[] strings) {
|
||||
String result = null;
|
||||
boolean finished = false;
|
||||
if (strings.length > 2) {
|
||||
String string = strings[0];
|
||||
if (string.equals(strings[1])) {
|
||||
finished = true;
|
||||
result = foo(string);
|
||||
}
|
||||
}
|
||||
if (!finished) {
|
||||
result = bar();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String test2(List<String> list, String foo, String bar) {
|
||||
String result = bar;
|
||||
boolean finished = false;
|
||||
for (String s : list) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (s.length() == i) {
|
||||
finished = true;
|
||||
result = foo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (finished) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String process(String s, int x) {
|
||||
String result = null;
|
||||
if (x > 0) {
|
||||
if (x == 2) {
|
||||
result = s.trim();
|
||||
} else {
|
||||
System.out.println(s.substring(0));
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = s.substring(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String process(String s, int x) {
|
||||
String result;
|
||||
if (x > 0) {
|
||||
if (x == 2) {
|
||||
result = s.trim();
|
||||
} else {
|
||||
result = s.substring(0);
|
||||
}
|
||||
} else {
|
||||
result = s.substring(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String process(String s) {
|
||||
String res = null;
|
||||
if (s != null) {
|
||||
s = s.trim();
|
||||
if (!s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
String result = s + s;
|
||||
res = result;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String process(String s) {
|
||||
String res = null;
|
||||
if (s != null) {
|
||||
s = s.trim();
|
||||
if (!s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
String result = s + s;
|
||||
res = result;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
void test2(String[] arr) {
|
||||
for (String s : arr) {
|
||||
if (s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
void test2(String[] arr) {
|
||||
boolean finished = false;
|
||||
for (String s : arr) {
|
||||
if (s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!finished) {
|
||||
System.out.println("Not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
void test2(String s) {
|
||||
if (s != null) {
|
||||
if (!s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean <caret>noEmptyStrings(String[][] list) {
|
||||
for (String[] inner : list) {
|
||||
for (String s : inner) {
|
||||
if (s.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean <caret>hasEmptyString(List<String> list) {
|
||||
for (String s : list) {
|
||||
if(s.isEmpty()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean <caret>test(String[] arr) {
|
||||
if (arr != null) {
|
||||
System.out.println("ok");
|
||||
for(String s : arr) {
|
||||
if (s.isEmpty()) return false;
|
||||
System.out.println(s);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean <caret>test(String[] arr) {
|
||||
if (arr == null) return false;
|
||||
String s = arr[0];
|
||||
if (s == null) return false;
|
||||
s = arr[1];
|
||||
if (s == null) return false;
|
||||
if (arr.length > 3) {
|
||||
s = arr[2];
|
||||
if (s != null && s.isEmpty()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
boolean <caret>test(String s) {
|
||||
if(s == null) return false;
|
||||
if(s.isEmpty()) return false;
|
||||
System.out.println(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String <caret>test2(List<String> list, String foo, String bar) {
|
||||
for(String s : list)
|
||||
for(int i=0; i<10; i++) {
|
||||
bar = s;
|
||||
if(s.length() == i) return foo;
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
native String get(String s);
|
||||
|
||||
String <caret>test(String[] data) {
|
||||
if (data == null) {
|
||||
return get("foo");
|
||||
}
|
||||
String s = data[0];
|
||||
int i=0;
|
||||
if (data.length > 2) {
|
||||
if (data[2] != null) {
|
||||
if(data[2].isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
if (!s.isEmpty()) {
|
||||
if (s.length() > 2) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
System.out.println(s);
|
||||
s = data[i++];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int <caret>test(String s) {
|
||||
if(s == null) {
|
||||
if (Math.random() > 0.5) return 2;
|
||||
return 4;
|
||||
}
|
||||
if(s.isEmpty()) return 3;
|
||||
System.out.println(s);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int <caret>test(String s) {
|
||||
if(s == null) {
|
||||
if (Math.random() > 0.5) return 2;
|
||||
System.out.println("going further");
|
||||
}
|
||||
if(s.isEmpty()) return 3;
|
||||
System.out.println(s);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int <caret>test(String[] strings) {
|
||||
for (String string : strings) {
|
||||
if (!string.isEmpty()) {
|
||||
return string.length(); // positive number
|
||||
}
|
||||
}
|
||||
return strings.length;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
int <caret>test(String[] strings) {
|
||||
for (String string : strings) {
|
||||
if (!string.equal("foo")) {
|
||||
return string.length(); // non-negative number
|
||||
}
|
||||
}
|
||||
return strings.length;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String <caret>test(String[] strings) {
|
||||
if (strings.length > 2) {
|
||||
String string = strings[0];
|
||||
if (string.equals(strings[1])) {
|
||||
return foo(string);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String <caret>test(String[] strings) {
|
||||
if (strings.length > 2) {
|
||||
String string = strings[0];
|
||||
if (string.equals(strings[1])) {
|
||||
return foo(string);
|
||||
}
|
||||
}
|
||||
String result = bar();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String <caret>test(String[] strings) {
|
||||
if (strings.length > 2) {
|
||||
String string = strings[0];
|
||||
if (string.equals(strings[1])) {
|
||||
return foo(string);
|
||||
}
|
||||
}
|
||||
return bar();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String <caret>test2(List<String> list, String foo, String bar) {
|
||||
for(String s : list)
|
||||
for(int i=0; i<10; i++) {
|
||||
if(s.length() == i) return foo;
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String<caret> process(String s, int x) {
|
||||
if (x > 0) {
|
||||
if (x == 2) {
|
||||
return s.trim();
|
||||
}
|
||||
System.out.println(s.substring(0));
|
||||
}
|
||||
return s.substring(1);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String<caret> process(String s, int x) {
|
||||
if (x > 0) {
|
||||
if (x == 2) {
|
||||
return s.trim();
|
||||
}
|
||||
return s.substring(0);
|
||||
}
|
||||
return s.substring(1);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String<caret> process(String s) {
|
||||
if (s == null) return null;
|
||||
s = s.trim();
|
||||
if (s.isEmpty()) return null;
|
||||
System.out.println(s);
|
||||
String result = s + s;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
String<caret> process(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
s = s.trim();
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
System.out.println(s);
|
||||
String result = s + s;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
void <caret>test2(String[] arr) {
|
||||
for(String s : arr) {
|
||||
if (s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
void <caret>test2(String[] arr) {
|
||||
for(String s : arr) {
|
||||
if (s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
System.out.println("Not found");
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Transform body to single exit-point form" "true"
|
||||
class Test {
|
||||
void <caret>test2(String s) {
|
||||
if(s == null) return;
|
||||
if(s.isEmpty()) return;
|
||||
System.out.println(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.intention;
|
||||
|
||||
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
|
||||
|
||||
public class ConvertToSingleReturnActionTest extends LightIntentionActionTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/convertToSingleReturn";
|
||||
}
|
||||
}
|
||||
@@ -574,4 +574,6 @@ collapse.selection.overlapping.warning.text=Overlapping fold region(s) exist
|
||||
collapse.selection.overlapping.warning.ok=Remove
|
||||
collapse.selection.overlapping.warning.cancel=Cancel
|
||||
|
||||
change.uid.action.name=Randomly change 'serialVersionUID' initializer
|
||||
change.uid.action.name=Randomly change 'serialVersionUID' initializer
|
||||
|
||||
intention.convert.to.single.return.name=Transform body to single exit-point form
|
||||
Reference in New Issue
Block a user