mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 11:50:54 +07:00
[java-refactoring] Refactor InlineMethod refactoring to use CodeBlockSurrounder
Added SurroundResult#collapse method to collapse expanded code block back. Now, InlineMethodProcessor is simpler and preserves semantics at the call site more often. Fixes IDEA-297364 Inline Method changes semantic for a method throwing exception GitOrigin-RevId: 2ceb23e372e2876202dac687048ac86fb23cfc3b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c2c6689387
commit
06d63c351a
@@ -87,6 +87,13 @@ public class ConditionalBreakInInfiniteLoopInspection extends AbstractBaseJavaLo
|
||||
};
|
||||
}
|
||||
|
||||
public static void tryTransform(@NotNull PsiWhileStatement whileStatement) {
|
||||
Context context = Context.from(whileStatement, true);
|
||||
if (context != null) {
|
||||
context.simplify(whileStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Context {
|
||||
final @NotNull PsiLoopStatement myLoopStatement;
|
||||
final @NotNull PsiStatement myLoopBody;
|
||||
@@ -194,6 +201,31 @@ public class ConditionalBreakInInfiniteLoopInspection extends AbstractBaseJavaLo
|
||||
.collect(Collectors.toSet());
|
||||
return !Collections.disjoint(variablesCreatedInBranch, otherVariablesUsedInLoop);
|
||||
}
|
||||
|
||||
private void simplify(@NotNull PsiConditionalLoopStatement loop) {
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String loopText;
|
||||
if (ControlFlowUtils.isEndlessLoop(loop)) {
|
||||
String conditionForWhile = this.myConditionInThen ? BoolUtils.getNegatedExpressionText(this.myCondition, ct) : ct.text(
|
||||
this.myCondition);
|
||||
LoopTransformationFix.pullDownStatements(this.myConditionStatement, this.myConditionInThen ? this.myConditionStatement.getElseBranch() : this.myConditionStatement.getThenBranch());
|
||||
ct.delete(this.myConditionStatement);
|
||||
loopText = this.myConditionInTheBeginning
|
||||
? "while(" + conditionForWhile + ")" + ct.text(this.myLoopBody)
|
||||
: "do" + ct.text(this.myLoopBody) + "while(" + conditionForWhile + ");";
|
||||
} else {
|
||||
String conditionForWhile = this.myConditionInThen ? BoolUtils.getNegatedExpressionText(this.myCondition, ParenthesesUtils.AND_PRECEDENCE, ct) : ct.text(
|
||||
this.myCondition, ParenthesesUtils.AND_PRECEDENCE);
|
||||
ct.delete(this.myConditionStatement);
|
||||
PsiExpression loopCondition = loop.getCondition();
|
||||
assert loopCondition != null;
|
||||
loopText = this.myConditionInTheBeginning
|
||||
? "while(" + ct.text(loopCondition, ParenthesesUtils.AND_PRECEDENCE) + " && " + conditionForWhile + ")" + ct.text(
|
||||
this.myLoopBody)
|
||||
: "do" + ct.text(this.myLoopBody) + "while(" + conditionForWhile + " && " + ct.text(loopCondition, ParenthesesUtils.AND_PRECEDENCE) + ");";
|
||||
}
|
||||
ct.replaceAndRestoreComments(this.myLoopStatement, loopText);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LoopTransformationFix implements LocalQuickFix {
|
||||
@@ -216,25 +248,7 @@ public class ConditionalBreakInInfiniteLoopInspection extends AbstractBaseJavaLo
|
||||
if (loop == null) return;
|
||||
Context context = Context.from(loop, noConversionToDoWhile);
|
||||
if (context == null) return;
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String loopText;
|
||||
if (ControlFlowUtils.isEndlessLoop(loop)) {
|
||||
String conditionForWhile = context.myConditionInThen ? BoolUtils.getNegatedExpressionText(context.myCondition, ct) : ct.text(context.myCondition);
|
||||
pullDownStatements(context.myConditionStatement, context.myConditionInThen ? context.myConditionStatement.getElseBranch() : context.myConditionStatement.getThenBranch());
|
||||
ct.delete(context.myConditionStatement);
|
||||
loopText = context.myConditionInTheBeginning
|
||||
? "while(" + conditionForWhile + ")" + ct.text(context.myLoopBody)
|
||||
: "do" + ct.text(context.myLoopBody) + "while(" + conditionForWhile + ");";
|
||||
} else {
|
||||
String conditionForWhile = context.myConditionInThen ? BoolUtils.getNegatedExpressionText(context.myCondition, ParenthesesUtils.AND_PRECEDENCE, ct) : ct.text(context.myCondition, ParenthesesUtils.AND_PRECEDENCE);
|
||||
ct.delete(context.myConditionStatement);
|
||||
PsiExpression loopCondition = loop.getCondition();
|
||||
assert loopCondition != null;
|
||||
loopText = context.myConditionInTheBeginning
|
||||
? "while(" + ct.text(loopCondition, ParenthesesUtils.AND_PRECEDENCE) + " && " + conditionForWhile + ")" + ct.text(context.myLoopBody)
|
||||
: "do" + ct.text(context.myLoopBody) + "while(" + conditionForWhile + " && " + ct.text(loopCondition, ParenthesesUtils.AND_PRECEDENCE) + ");";
|
||||
}
|
||||
ct.replaceAndRestoreComments(context.myLoopStatement, loopText);
|
||||
context.simplify(loop);
|
||||
}
|
||||
|
||||
private static void pullDownStatements(@NotNull PsiIfStatement conditionStatement, @Nullable PsiStatement branch) {
|
||||
@@ -26,7 +26,6 @@ import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
@@ -41,7 +40,6 @@ import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.introduceParameter.Util;
|
||||
import com.intellij.refactoring.listeners.RefactoringEventData;
|
||||
import com.intellij.refactoring.rename.NonCodeUsageInfoFactory;
|
||||
import com.intellij.refactoring.rename.RenameJavaMemberProcessor;
|
||||
import com.intellij.refactoring.util.*;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
@@ -54,7 +52,7 @@ import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.psiutils.CodeBlockSurrounder;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -76,14 +74,11 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
private final boolean myDeleteTheDeclaration;
|
||||
private final Function<PsiReference, InlineTransformer> myTransformerChooser;
|
||||
|
||||
private final PsiManager myManager;
|
||||
private final PsiElementFactory myFactory;
|
||||
private final CodeStyleManager myCodeStyleManager;
|
||||
private final JavaCodeStyleManager myJavaCodeStyle;
|
||||
|
||||
private PsiCodeBlock[] myAddedBraces;
|
||||
private final String myDescriptiveName;
|
||||
private Map<PsiField, PsiClassInitializer> myAddedClassInitializers;
|
||||
private List<CodeBlockSurrounder.SurroundResult> mySurroundResults;
|
||||
private PsiMethod myMethodCopy;
|
||||
@SuppressWarnings("LeakableMapKey") //short living refactoring
|
||||
private Map<Language, InlineHandler.Inliner> myInliners;
|
||||
@@ -124,9 +119,8 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
mySearchForTextOccurrences = searchForTextOccurrences;
|
||||
myDeleteTheDeclaration = isDeleteTheDeclaration;
|
||||
|
||||
myManager = PsiManager.getInstance(myProject);
|
||||
myFactory = JavaPsiFacade.getElementFactory(myManager.getProject());
|
||||
myCodeStyleManager = CodeStyleManager.getInstance(myProject);
|
||||
PsiManager manager = PsiManager.getInstance(myProject);
|
||||
myFactory = JavaPsiFacade.getElementFactory(manager.getProject());
|
||||
myJavaCodeStyle = JavaCodeStyleManager.getInstance(myProject);
|
||||
myDescriptiveName = DescriptiveNameUtil.getDescriptiveName(myMethod);
|
||||
}
|
||||
@@ -187,9 +181,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
|
||||
private boolean shouldDeleteOverrideAttribute(PsiMethod method) {
|
||||
return method.getHierarchicalMethodSignature()
|
||||
.getSuperSignatures().stream()
|
||||
.allMatch(signature -> {
|
||||
return ContainerUtil.and(method.getHierarchicalMethodSignature().getSuperSignatures(), signature -> {
|
||||
PsiMethod superMethod = signature.getMethod();
|
||||
if (superMethod == myMethod) {
|
||||
return true;
|
||||
@@ -461,7 +453,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
}
|
||||
else {
|
||||
myReference = addBracesWhenNeeded(new PsiReferenceExpression[]{(PsiReferenceExpression)myReference})[0];
|
||||
myReference = surroundWithCodeBlock(new PsiReferenceExpression[]{(PsiReferenceExpression)myReference})[0];
|
||||
if (myReference instanceof PsiMethodReferenceExpression) {
|
||||
inlineMethodReference((PsiMethodReferenceExpression)myReference);
|
||||
}
|
||||
@@ -523,7 +515,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
}
|
||||
PsiReferenceExpression[] refs = refExprList.toArray(new PsiReferenceExpression[0]);
|
||||
refs = addBracesWhenNeeded(refs);
|
||||
refs = surroundWithCodeBlock(refs);
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
if (ref instanceof PsiMethodReferenceExpression) {
|
||||
inlineMethodReference((PsiMethodReferenceExpression)ref);
|
||||
@@ -545,7 +537,11 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
tracker.deleteAndRestoreComments(myMethod);
|
||||
}
|
||||
}
|
||||
removeAddedBracesWhenPossible();
|
||||
if (mySurroundResults != null) {
|
||||
for (CodeBlockSurrounder.SurroundResult result : mySurroundResults) {
|
||||
result.collapse();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
@@ -662,31 +658,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
|
||||
PsiElement anchor = CommonJavaRefactoringUtil.getParentStatement(methodCall, true);
|
||||
if (anchor == null) {
|
||||
PsiEnumConstant enumConstant = PsiTreeUtil.getParentOfType(methodCall, PsiEnumConstant.class);
|
||||
if (enumConstant != null) {
|
||||
PsiExpression returnExpr = getSimpleReturnedExpression(myMethod);
|
||||
if (returnExpr != null) {
|
||||
ChangeContextUtil.encodeContextInfo(returnExpr, true);
|
||||
PsiElement copy = returnExpr.copy();
|
||||
ChangeContextUtil.clearContextInfo(returnExpr);
|
||||
if (copy instanceof PsiReferenceExpression && ((PsiReferenceExpression)copy).getQualifierExpression() == null) {
|
||||
copy = inlineParameterReference((PsiReferenceExpression)copy, blockData);
|
||||
} else {
|
||||
copy.accept(new JavaRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
|
||||
super.visitReferenceExpression(expression);
|
||||
inlineParameterReference(expression, blockData);
|
||||
}
|
||||
});
|
||||
}
|
||||
PsiElement replace = methodCall.replace(copy);
|
||||
if (blockData.thisVar != null) {
|
||||
ChangeContextUtil.decodeContextInfo(replace, myMethod.getContainingClass(), blockData.thisVar.getInitializer());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
throw new IllegalStateException("Cannot inline: parent statement should be available after CodeBlockSurround");
|
||||
}
|
||||
PsiElement anchorParent = anchor.getParent();
|
||||
PsiLocalVariable thisVar = null;
|
||||
@@ -930,145 +902,44 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Key<String> MARK_KEY = Key.create("");
|
||||
private static final Key<PsiReferenceExpression> MARK_KEY = Key.create("MarkForSurround");
|
||||
|
||||
private PsiReferenceExpression[] addBracesWhenNeeded(PsiReferenceExpression[] refs) throws IncorrectOperationException {
|
||||
ArrayList<PsiReferenceExpression> refsVector = new ArrayList<>();
|
||||
ArrayList<PsiCodeBlock> addedBracesVector = new ArrayList<>();
|
||||
myAddedClassInitializers = new HashMap<>();
|
||||
private PsiReferenceExpression[] surroundWithCodeBlock(PsiReferenceExpression[] refs) throws IncorrectOperationException {
|
||||
mySurroundResults = new ArrayList<>();
|
||||
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
if (ref instanceof PsiMethodReferenceExpression) continue;
|
||||
ref.putCopyableUserData(MARK_KEY, "");
|
||||
ref.putCopyableUserData(MARK_KEY, ref);
|
||||
}
|
||||
var visitor = new PsiRecursiveElementWalkingVisitor() {
|
||||
final Map<PsiReferenceExpression, PsiReferenceExpression> mapping = new HashMap<>();
|
||||
|
||||
RefLoop:
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
if (!ref.isValid()) continue;
|
||||
if (ref instanceof PsiMethodReferenceExpression) {
|
||||
refsVector.add(ref);
|
||||
continue;
|
||||
@Override
|
||||
public void visitElement(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression orig = element.getCopyableUserData(MARK_KEY);
|
||||
if (orig != null) {
|
||||
mapping.put(orig, (PsiReferenceExpression)element);
|
||||
}
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
|
||||
PsiElement parentStatement = CommonJavaRefactoringUtil.getParentStatement(ref, true);
|
||||
if (parentStatement != null) {
|
||||
PsiElement parent = ref.getParent();
|
||||
while (!parent.equals(parentStatement)) {
|
||||
if (parent instanceof PsiExpressionStatement || parent instanceof PsiReturnStatement) {
|
||||
String text = "{\n}";
|
||||
PsiBlockStatement blockStatement = (PsiBlockStatement)myFactory.createStatementFromText(text, null);
|
||||
blockStatement = (PsiBlockStatement)myCodeStyleManager.reformat(blockStatement);
|
||||
blockStatement.getCodeBlock().add(parent);
|
||||
blockStatement = (PsiBlockStatement)parent.replace(blockStatement);
|
||||
|
||||
PsiElement newStatement = blockStatement.getCodeBlock().getStatements()[0];
|
||||
addMarkedElements(refsVector, newStatement);
|
||||
addedBracesVector.add(blockStatement.getCodeBlock());
|
||||
continue RefLoop;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
final PsiElement lambdaExpr = parentStatement.getParent();
|
||||
if (lambdaExpr instanceof PsiLambdaExpression) {
|
||||
final PsiLambdaExpression newLambdaExpr = (PsiLambdaExpression)myFactory.createExpressionFromText(
|
||||
((PsiLambdaExpression)lambdaExpr).getParameterList().getText() + " -> " + "{\n}", lambdaExpr);
|
||||
final PsiStatement statementFromText;
|
||||
if (PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)lambdaExpr))) {
|
||||
statementFromText = myFactory.createStatementFromText("a;", lambdaExpr);
|
||||
((PsiExpressionStatement)statementFromText).getExpression().replace(parentStatement);
|
||||
} else {
|
||||
statementFromText = myFactory.createStatementFromText("return a;", lambdaExpr);
|
||||
((PsiReturnStatement)statementFromText).getReturnValue().replace(parentStatement);
|
||||
}
|
||||
|
||||
newLambdaExpr.getBody().add(statementFromText);
|
||||
|
||||
final PsiCodeBlock body = (PsiCodeBlock)((PsiLambdaExpression)lambdaExpr.replace(newLambdaExpr)).getBody();
|
||||
PsiElement newStatement = body.getStatements()[0];
|
||||
addMarkedElements(refsVector, newStatement);
|
||||
addedBracesVector.add(body);
|
||||
continue;
|
||||
}
|
||||
else if (lambdaExpr instanceof PsiSwitchLabeledRuleStatement && parentStatement instanceof PsiExpressionStatement) {
|
||||
CodeBlockSurrounder surrounder = CodeBlockSurrounder.forExpression(ref);
|
||||
if (surrounder != null) {
|
||||
CodeBlockSurrounder.SurroundResult surround = surrounder.surround();
|
||||
PsiExpression expression = surround.getExpression();
|
||||
addMarkedElements(refsVector, expression);
|
||||
ContainerUtil.addIfNotNull(addedBracesVector, PsiTreeUtil.getParentOfType(surround.getAnchor(), PsiCodeBlock.class));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final PsiField field = PsiTreeUtil.getParentOfType(ref, PsiField.class);
|
||||
if (field != null) {
|
||||
if (field instanceof PsiEnumConstant) {
|
||||
inlineEnumConstantParameter(refsVector, ref);
|
||||
continue;
|
||||
}
|
||||
if (myAddedClassInitializers.containsKey(field)) {
|
||||
continue;
|
||||
}
|
||||
field.normalizeDeclaration();
|
||||
final PsiExpression initializer = field.getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
PsiClassInitializer classInitializer = myFactory.createClassInitializer();
|
||||
final PsiClass containingClass = field.getContainingClass();
|
||||
classInitializer = (PsiClassInitializer)containingClass.addAfter(classInitializer, field);
|
||||
containingClass.addAfter(CodeEditUtil.createLineFeed(field.getManager()), field);
|
||||
final PsiCodeBlock body = classInitializer.getBody();
|
||||
PsiExpressionStatement statement = (PsiExpressionStatement)myFactory.createStatementFromText(field.getName() + " = 0;", body);
|
||||
statement = (PsiExpressionStatement)body.add(statement);
|
||||
final PsiAssignmentExpression assignment = (PsiAssignmentExpression)statement.getExpression();
|
||||
assignment.getLExpression().replace(RenameJavaMemberProcessor.createMemberReference(field, assignment));
|
||||
assignment.getRExpression().replace(initializer);
|
||||
addMarkedElements(refsVector, statement);
|
||||
if (field.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
PsiUtil.setModifierProperty(classInitializer, PsiModifier.STATIC, true);
|
||||
}
|
||||
myAddedClassInitializers.put(field, classInitializer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
refsVector.add(ref);
|
||||
}
|
||||
};
|
||||
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
ref.putCopyableUserData(MARK_KEY, null);
|
||||
}
|
||||
if (!ref.isValid() || ref instanceof PsiMethodReferenceExpression) continue;
|
||||
|
||||
myAddedBraces = addedBracesVector.toArray(PsiCodeBlock.EMPTY_ARRAY);
|
||||
return refsVector.toArray(new PsiReferenceExpression[0]);
|
||||
}
|
||||
|
||||
private void inlineEnumConstantParameter(final List<? super PsiReferenceExpression> refsVector,
|
||||
final PsiReferenceExpression ref) throws IncorrectOperationException {
|
||||
PsiExpression expr = getSimpleReturnedExpression(myMethod);
|
||||
if (expr != null) {
|
||||
refsVector.add(ref);
|
||||
}
|
||||
else {
|
||||
PsiCall call = PsiTreeUtil.getParentOfType(ref, PsiCall.class);
|
||||
@NonNls String text = "new Object() { " + myMethod.getReturnTypeElement().getText() + " evaluate() { return " + call.getText() + ";}}.evaluate";
|
||||
PsiExpression callExpr = JavaPsiFacade.getInstance(myProject).getParserFacade().createExpressionFromText(text, call);
|
||||
PsiReferenceExpression classExpr = (PsiReferenceExpression)ref.replace(callExpr);
|
||||
PsiNewExpression newObject = (PsiNewExpression)Objects.requireNonNull(classExpr.getQualifierExpression());
|
||||
PsiMethod evaluateMethod = Objects.requireNonNull(newObject.getAnonymousClass()).getMethods()[0];
|
||||
PsiExpression retVal = ((PsiReturnStatement)Objects.requireNonNull(evaluateMethod.getBody())
|
||||
.getStatements()[0]).getReturnValue();
|
||||
if (retVal instanceof PsiMethodCallExpression) {
|
||||
refsVector.add(((PsiMethodCallExpression) retVal).getMethodExpression());
|
||||
}
|
||||
if (classExpr.getParent() instanceof PsiMethodCallExpression) {
|
||||
PsiExpressionList args = ((PsiMethodCallExpression)classExpr.getParent()).getArgumentList();
|
||||
PsiExpression[] argExpressions = args.getExpressions();
|
||||
if (argExpressions.length > 0) {
|
||||
args.deleteChildRange(argExpressions [0], argExpressions [argExpressions.length-1]);
|
||||
}
|
||||
CodeBlockSurrounder surrounder = CodeBlockSurrounder.forExpression(ref);
|
||||
if (surrounder != null) {
|
||||
CodeBlockSurrounder.SurroundResult surround = surrounder.surround();
|
||||
surround.getAnchor().accept(visitor);
|
||||
mySurroundResults.add(surround);
|
||||
}
|
||||
}
|
||||
|
||||
return StreamEx.of(refs).map(ref -> visitor.mapping.getOrDefault(ref, ref))
|
||||
.peek(ref -> ref.putCopyableUserData(MARK_KEY, null))
|
||||
.toArray(new PsiReferenceExpression[0]);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1082,91 +953,6 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
return ((PsiReturnStatement) statement).getReturnValue();
|
||||
}
|
||||
|
||||
private static void addMarkedElements(final List<? super PsiReferenceExpression> array, PsiElement scope) {
|
||||
scope.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
@Override public void visitElement(@NotNull PsiElement element) {
|
||||
if (element.getCopyableUserData(MARK_KEY) != null) {
|
||||
array.add((PsiReferenceExpression)element);
|
||||
element.putCopyableUserData(MARK_KEY, null);
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeAddedBracesWhenPossible() throws IncorrectOperationException {
|
||||
if (myAddedBraces == null) return;
|
||||
|
||||
for (PsiCodeBlock codeBlock : myAddedBraces) {
|
||||
PsiStatement[] statements = codeBlock.getStatements();
|
||||
if (statements.length == 1) {
|
||||
final PsiElement codeBlockParent = codeBlock.getParent();
|
||||
PsiStatement statement = statements[0];
|
||||
if (codeBlockParent instanceof PsiLambdaExpression) {
|
||||
if (statement instanceof PsiReturnStatement) {
|
||||
final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue();
|
||||
if (returnValue != null) {
|
||||
codeBlock.replace(returnValue);
|
||||
}
|
||||
} else if (statement instanceof PsiExpressionStatement){
|
||||
codeBlock.replace(((PsiExpressionStatement)statement).getExpression());
|
||||
}
|
||||
}
|
||||
else if (codeBlockParent instanceof PsiBlockStatement) {
|
||||
if (statement instanceof PsiYieldStatement) {
|
||||
PsiExpression expression = ((PsiYieldStatement)statement).getExpression();
|
||||
if (expression != null) {
|
||||
PsiExpressionStatement statementFromText = (PsiExpressionStatement)myFactory.createStatementFromText("a;", expression);
|
||||
statementFromText.getExpression().replace(expression);
|
||||
codeBlockParent.replace(statementFromText);
|
||||
}
|
||||
} else if (!(ifStatementWithAppendableElseBranch(statement) &&
|
||||
codeBlockParent.getParent() instanceof PsiIfStatement &&
|
||||
((PsiIfStatement)codeBlockParent.getParent()).getElseBranch() != null)) {
|
||||
codeBlockParent.replace(statement);
|
||||
}
|
||||
}
|
||||
else {
|
||||
codeBlock.replace(statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myAddedClassInitializers.forEach((psiField, classInitializer) -> {
|
||||
PsiExpression newInitializer = getSimpleFieldInitializer(psiField, classInitializer);
|
||||
PsiExpression fieldInitializer = Objects.requireNonNull(psiField.getInitializer());
|
||||
if (newInitializer != null) {
|
||||
fieldInitializer.replace(newInitializer);
|
||||
classInitializer.delete();
|
||||
}
|
||||
else {
|
||||
fieldInitializer.delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean ifStatementWithAppendableElseBranch(PsiStatement statement) {
|
||||
if (statement instanceof PsiIfStatement) {
|
||||
PsiStatement elseBranch = ((PsiIfStatement)statement).getElseBranch();
|
||||
return elseBranch == null || elseBranch instanceof PsiIfStatement;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiExpression getSimpleFieldInitializer(PsiField field, PsiClassInitializer initializer) {
|
||||
final PsiStatement[] statements = initializer.getBody().getStatements();
|
||||
if (statements.length != 1) return null;
|
||||
if (!(statements[0] instanceof PsiExpressionStatement)) return null;
|
||||
final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
|
||||
if (!(expression instanceof PsiAssignmentExpression)) return null;
|
||||
final PsiExpression lExpression = ((PsiAssignmentExpression)expression).getLExpression();
|
||||
if (!(lExpression instanceof PsiReferenceExpression)) return null;
|
||||
final PsiElement resolved = ((PsiReferenceExpression)lExpression).resolve();
|
||||
if (!myManager.areElementsEquivalent(field, resolved)) return null;
|
||||
return ((PsiAssignmentExpression)expression).getRExpression();
|
||||
}
|
||||
|
||||
public static @NlsContexts.DialogMessage String checkUnableToInsertCodeBlock(PsiCodeBlock methodBody, final PsiElement element) {
|
||||
if (checkUnableToInsertCodeBlock(methodBody, element,
|
||||
expr -> JavaPsiConstructorUtil.isConstructorCall(expr) && expr.getMethodExpression() != element)) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class MyQueue {
|
||||
private long[] items = new long[16];
|
||||
|
||||
private int head;
|
||||
private int tail;
|
||||
|
||||
public boolean isEmpty() { return head == tail; }
|
||||
|
||||
public long getFirst() {
|
||||
if (isEmpty())
|
||||
throw new NoSuchElementException("Queue is empty");
|
||||
return items[head];
|
||||
}
|
||||
|
||||
// true if the first element of the queue is greater than specified number
|
||||
public boolean firstIsGreaterThan(int x) {
|
||||
return !isEmpty() && g<caret>etFirst() > x;
|
||||
}
|
||||
|
||||
// other methods
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class MyQueue {
|
||||
private long[] items = new long[16];
|
||||
|
||||
private int head;
|
||||
private int tail;
|
||||
|
||||
public boolean isEmpty() { return head == tail; }
|
||||
|
||||
// true if the first element of the queue is greater than specified number
|
||||
public boolean firstIsGreaterThan(int x) {
|
||||
if (isEmpty()) return false;
|
||||
if (isEmpty())
|
||||
throw new NoSuchElementException("Queue is empty");
|
||||
return items[head] > x;
|
||||
}
|
||||
|
||||
// other methods
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class X {
|
||||
Predicate<String> predicate() {
|
||||
return s -> !s.isEmpty() && <caret>test(s) > 0;
|
||||
}
|
||||
|
||||
int test(String s) {
|
||||
int length = s.length();
|
||||
int ch = s.charAt(0);
|
||||
return length + ch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class X {
|
||||
Predicate<String> predicate() {
|
||||
return s -> {
|
||||
if (s.isEmpty()) return false;
|
||||
int length = s.length();
|
||||
int ch = s.charAt(0);
|
||||
return length + ch > 0;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class X {
|
||||
Predicate<String> predicate() {
|
||||
return s -> !s.isEmpty() && <caret>test(s) > 0;
|
||||
}
|
||||
|
||||
int test(String s) {
|
||||
return s.length() + s.charAt(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class X {
|
||||
Predicate<String> predicate() {
|
||||
return s -> !s.isEmpty() && s.length() + s.charAt(0) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
class Foo {
|
||||
|
||||
public static final Bar[] bars= new Bar[] {new Bar("a", 0, "A", "B", "C"), new Bar("b", 1, "A", "B")};
|
||||
public static final Bar[] bars = new Bar[] {new Bar("a", 0, "A", "B", "C"), new Bar("b", 1, "A", "B")};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ class MyWorld {
|
||||
public void process(Object[] o) {
|
||||
int i = 0;
|
||||
while (o[i] instanceof String && ((String) o[i]).startsWith("hello")) {
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
while (!(o[i] instanceof String && ((String) o[i]).startsWith("hello"))) {
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class X {
|
||||
int x = Math.random() > 0.5 ? te<caret>st(1, 2, 3) + 1 : 0;
|
||||
|
||||
int test(int a, int b, int c) {
|
||||
int one = a * a + b * b;
|
||||
int two = c * c;
|
||||
return one / two;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class X {
|
||||
int x;
|
||||
|
||||
{
|
||||
if (Math.random() > 0.5) {
|
||||
int one = 1 * 1 + 2 * 2;
|
||||
int two = 3 * 3;
|
||||
x = one / two + 1;
|
||||
} else {
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class X {
|
||||
int x = Math.random() > 0.5 ? te<caret>st(1, 2, 3) + 1 : 0;
|
||||
|
||||
int test(int a, int b, int c) {
|
||||
return (a * a + b * b) / (c * c);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class X {
|
||||
int x = Math.random() > 0.5 ? (1 * 1 + 2 * 2) / (3 * 3) + 1 : 0;
|
||||
|
||||
}
|
||||
@@ -4,8 +4,7 @@ class Temp {
|
||||
}
|
||||
|
||||
public Object foo(Set bar) {
|
||||
if (bar.size() < 2) {
|
||||
// Inline this
|
||||
if (bar.size() < 2) {// Inline this
|
||||
bar.size(); // or online this
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import com.intellij.testFramework.IdeaTestUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -554,6 +556,13 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testInSwitchExpression() { doTest(); }
|
||||
public void testInSwitchExpressionYield() { doTest(); }
|
||||
|
||||
public void testAndChain() { doTest(); }
|
||||
public void testAndChainLambda() { doTest(); }
|
||||
public void testAndChainLambdaSingleLine() { doTest(); }
|
||||
|
||||
public void testTernaryBranch() { doTest(); }
|
||||
public void testTernaryBranchCollapsible() { doTest(); }
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return getTestName(false).contains("Src") ? IdeaTestUtil.getMockJdk17() : super.getProjectJDK();
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.siyeh.ig.psiutils;
|
||||
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.intention.impl.SplitConditionUtil;
|
||||
import com.intellij.codeInspection.ConditionalBreakInInfiniteLoopInspection;
|
||||
import com.intellij.codeInspection.RedundantLambdaCodeBlockInspection;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
@@ -14,6 +16,8 @@ import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.style.ConditionalExpressionGenerator;
|
||||
import com.siyeh.ig.style.IfConditionalModel;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -24,9 +28,11 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* A facility to ensure that there's a code block that surrounds given expression, so if parts of expression should be extracted
|
||||
* to separate statements, these is a place where new statements could be generated.
|
||||
* to separate statements, there is a place where new statements could be generated.
|
||||
* <p>
|
||||
* The lifecycle is the following:
|
||||
* <ol>
|
||||
@@ -70,10 +76,14 @@ public abstract class CodeBlockSurrounder {
|
||||
public static class SurroundResult {
|
||||
private final @NotNull PsiExpression myExpression;
|
||||
private final @NotNull PsiStatement myAnchor;
|
||||
private final @NotNull PsiElement myContext;
|
||||
private final @Nullable CodeBlockSurrounder mySurrounder;
|
||||
|
||||
SurroundResult(@NotNull PsiExpression expression, @NotNull PsiStatement anchor) {
|
||||
SurroundResult(@NotNull PsiExpression expression, @NotNull PsiStatement anchor, @NotNull PsiElement context, @Nullable CodeBlockSurrounder surrounder) {
|
||||
myExpression = expression;
|
||||
myAnchor = anchor;
|
||||
myContext = context;
|
||||
mySurrounder = surrounder;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +99,17 @@ public abstract class CodeBlockSurrounder {
|
||||
public @NotNull PsiStatement getAnchor() {
|
||||
return myAnchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to collapse the created code-block back if it still consists of a single statement.
|
||||
* This method could be called after the whole refactoring is completed if it's possible that additional statements
|
||||
* were not actually necessary.
|
||||
*/
|
||||
public void collapse() {
|
||||
if (mySurrounder != null && myContext.isValid()) {
|
||||
mySurrounder.collapse(myContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final @NotNull PsiExpression myExpression;
|
||||
@@ -143,14 +164,38 @@ public abstract class CodeBlockSurrounder {
|
||||
boolean physical = myExpression.isPhysical();
|
||||
PsiStatement replacement = replace(project, factory);
|
||||
assert replacement.isPhysical() == physical;
|
||||
PsiElement anchor = anchor(replacement);
|
||||
PsiExpression newExpression = Objects.requireNonNull((PsiExpression)PsiTreeUtil.releaseMark(replacement, marker));
|
||||
return new SurroundResult(newExpression, replacement);
|
||||
return new SurroundResult(newExpression, replacement, anchor, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the replacement
|
||||
*
|
||||
* @param project project to use
|
||||
* @param factory factory to use
|
||||
* @return PSI statement, a place before which it's safe to add more statements
|
||||
*/
|
||||
@NotNull PsiStatement replace(@NotNull Project project, @NotNull PsiElementFactory factory) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context PSI statement returned from replace
|
||||
* @return anchor a stable PSI anchor for subsequent use in {@link #collapse(PsiElement)}. Could be a statement itself,
|
||||
* but there's a risk that intermediate refactoring makes it invalid, so it's better to return something bigger
|
||||
* (e.g., a PsiBlockStatement created).
|
||||
*/
|
||||
@NotNull PsiElement anchor(@NotNull PsiStatement context) {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to collapse back the block if it becomes unnecessary
|
||||
* @param anchor anchor previously returned by {@link #anchor(PsiStatement)}
|
||||
*/
|
||||
void collapse(@NotNull PsiElement anchor) {}
|
||||
|
||||
/**
|
||||
* @param expression expression to test
|
||||
* @return true if expression can be surrounded with a code block or already has a surrounding code block
|
||||
@@ -209,6 +254,9 @@ public abstract class CodeBlockSurrounder {
|
||||
cur = parent;
|
||||
parent = cur.getParent();
|
||||
}
|
||||
if (parent instanceof PsiEnumConstant) {
|
||||
return new AnonymousCallSurrounder(expression);
|
||||
}
|
||||
if (parent instanceof PsiStatement) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (grandParent instanceof PsiForStatement && ((PsiForStatement)grandParent).getUpdate() == parent) return null;
|
||||
@@ -223,7 +271,7 @@ public abstract class CodeBlockSurrounder {
|
||||
return forStatement((PsiStatement)parent, expression);
|
||||
}
|
||||
if (parent instanceof PsiLocalVariable) {
|
||||
PsiDeclarationStatement decl = ObjectUtils.tryCast(parent.getParent(), PsiDeclarationStatement.class);
|
||||
PsiDeclarationStatement decl = tryCast(parent.getParent(), PsiDeclarationStatement.class);
|
||||
if (decl != null && ArrayUtil.getFirstElement(decl.getDeclaredElements()) == parent) {
|
||||
PsiTypeElement typeElement = ((PsiLocalVariable)parent).getTypeElement();
|
||||
if (!typeElement.isInferredType() ||
|
||||
@@ -241,7 +289,7 @@ public abstract class CodeBlockSurrounder {
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiResourceVariable) {
|
||||
PsiResourceList list = ObjectUtils.tryCast(parent.getParent(), PsiResourceList.class);
|
||||
PsiResourceList list = tryCast(parent.getParent(), PsiResourceList.class);
|
||||
if (list != null && list.getParent() instanceof PsiTryStatement) {
|
||||
Iterator<PsiResourceListElement> iterator = list.iterator();
|
||||
PsiTryStatement tryStatement = (PsiTryStatement)list.getParent();
|
||||
@@ -253,7 +301,7 @@ public abstract class CodeBlockSurrounder {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (parent instanceof PsiField && !(parent instanceof PsiEnumConstant)) {
|
||||
if (parent instanceof PsiField) {
|
||||
return new ExtractFieldInitializerSurrounder(expression, (PsiField)parent);
|
||||
}
|
||||
|
||||
@@ -281,7 +329,7 @@ public abstract class CodeBlockSurrounder {
|
||||
|
||||
private static CodeBlockSurrounder forStatement(PsiStatement statement, PsiExpression expression) {
|
||||
PsiElement statementParent = statement.getParent();
|
||||
PsiForStatement forStatement = ObjectUtils.tryCast(statementParent, PsiForStatement.class);
|
||||
PsiForStatement forStatement = tryCast(statementParent, PsiForStatement.class);
|
||||
if (statementParent instanceof PsiLabeledStatement || (forStatement != null && forStatement.getBody() != statement)) {
|
||||
statement = (PsiStatement)statementParent;
|
||||
statementParent = statement.getParent();
|
||||
@@ -306,10 +354,65 @@ public abstract class CodeBlockSurrounder {
|
||||
|
||||
@Override
|
||||
public @NotNull CodeBlockSurrounder.SurroundResult surround() {
|
||||
return new SurroundResult(myExpression, myAnchor);
|
||||
return new SurroundResult(myExpression, myAnchor, myAnchor, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnonymousCallSurrounder extends CodeBlockSurrounder {
|
||||
AnonymousCallSurrounder(@NotNull PsiExpression expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
PsiStatement replace(@NotNull Project project, @NotNull PsiElementFactory factory) {
|
||||
PsiExpression expression = myExpression;
|
||||
if (expression.getParent() instanceof PsiMethodCallExpression &&
|
||||
((PsiMethodCallExpression)expression.getParent()).getMethodExpression() == expression) {
|
||||
expression = (PsiExpression)expression.getParent();
|
||||
}
|
||||
PsiType type = expression.getType();
|
||||
String typeText = type == null ? CommonClassNames.JAVA_LANG_OBJECT : type.getCanonicalText();
|
||||
String text = "new java.lang.Object() { " + typeText + " evaluate() { return x;}}.evaluate()";
|
||||
PsiMethodCallExpression anonymousCall =
|
||||
(PsiMethodCallExpression)JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(text, expression);
|
||||
PsiReturnStatement returnStatement = Objects.requireNonNull(getReturnStatement(anonymousCall));
|
||||
Objects.requireNonNull(returnStatement.getReturnValue()).replace(expression);
|
||||
return Objects.requireNonNull(getReturnStatement((PsiMethodCallExpression)expression.replace(anonymousCall)));
|
||||
}
|
||||
|
||||
private static @Nullable PsiReturnStatement getReturnStatement(@NotNull PsiMethodCallExpression anonymousCall) {
|
||||
PsiNewExpression newExpression = tryCast(anonymousCall.getMethodExpression().getQualifierExpression(), PsiNewExpression.class);
|
||||
if (newExpression == null) return null;
|
||||
PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
|
||||
if (anonymousClass == null) return null;
|
||||
PsiMethod[] methods = anonymousClass.getMethods();
|
||||
if (methods.length != 1) return null;
|
||||
PsiCodeBlock body = methods[0].getBody();
|
||||
if (body == null) return null;
|
||||
PsiStatement[] statements = body.getStatements();
|
||||
if (statements.length != 1) return null;
|
||||
return tryCast(statements[0], PsiReturnStatement.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull PsiMethodCallExpression anchor(@NotNull PsiStatement context) {
|
||||
PsiMethod method = (PsiMethod)context.getParent().getParent();
|
||||
PsiNewExpression newExpression = (PsiNewExpression)method.getParent().getParent();
|
||||
return (PsiMethodCallExpression) newExpression.getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiMethodCallExpression anonymousCall = tryCast(anchor, PsiMethodCallExpression.class);
|
||||
if (anonymousCall == null) return;
|
||||
PsiReturnStatement returnStatement = getReturnStatement(anonymousCall);
|
||||
if (returnStatement == null) return;
|
||||
PsiExpression targetExpression = returnStatement.getReturnValue();
|
||||
if (targetExpression == null) return;
|
||||
new CommentTracker().replaceAndRestoreComments(anonymousCall, targetExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LambdaCodeBlockSurrounder extends CodeBlockSurrounder {
|
||||
private final @NotNull PsiLambdaExpression myLambda;
|
||||
@@ -338,6 +441,22 @@ public abstract class CodeBlockSurrounder {
|
||||
newBody = (PsiCodeBlock)myLambda.getBody().replace(newBody);
|
||||
return newBody.getStatements()[0]; // either expression statement or return statement
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull PsiLambdaExpression anchor(@NotNull PsiStatement context) {
|
||||
return (PsiLambdaExpression)context.getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiLambdaExpression lambda = tryCast(anchor, PsiLambdaExpression.class);
|
||||
if (lambda == null) return;
|
||||
PsiElement body = lambda.getBody();
|
||||
PsiExpression expression = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(body);
|
||||
if (expression != null) {
|
||||
body.replace(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class YieldSurrounder extends CodeBlockSurrounder {
|
||||
@@ -361,6 +480,23 @@ public abstract class CodeBlockSurrounder {
|
||||
block = (PsiBlockStatement)myStatement.replace(block);
|
||||
return block.getCodeBlock().getStatements()[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull PsiBlockStatement anchor(@NotNull PsiStatement context) {
|
||||
return (PsiBlockStatement)context.getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiBlockStatement blockStatement = tryCast(anchor, PsiBlockStatement.class);
|
||||
if (blockStatement == null) return;
|
||||
PsiStatement statement = ControlFlowUtils.getOnlyStatementInBlock(blockStatement.getCodeBlock());
|
||||
if (!(statement instanceof PsiYieldStatement)) return;
|
||||
PsiExpression expression = ((PsiYieldStatement)statement).getExpression();
|
||||
if (expression == null) return;
|
||||
CommentTracker ct = new CommentTracker();
|
||||
ct.replaceAndRestoreComments(blockStatement, ct.text(expression) + ";");
|
||||
}
|
||||
}
|
||||
|
||||
private static class SimpleSurrounder extends CodeBlockSurrounder {
|
||||
@@ -378,6 +514,26 @@ public abstract class CodeBlockSurrounder {
|
||||
block = (PsiBlockStatement)myStatement.replace(block);
|
||||
return block.getCodeBlock().getStatements()[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull PsiBlockStatement anchor(@NotNull PsiStatement context) {
|
||||
return (PsiBlockStatement)context.getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiBlockStatement blockStatement = tryCast(anchor, PsiBlockStatement.class);
|
||||
if (blockStatement == null) return;
|
||||
PsiStatement statement = ControlFlowUtils.getOnlyStatementInBlock(blockStatement.getCodeBlock());
|
||||
if (statement == null) return;
|
||||
if (statement instanceof PsiIfStatement &&
|
||||
blockStatement.getParent() instanceof PsiIfStatement &&
|
||||
((PsiIfStatement)blockStatement.getParent()).getThenBranch() == blockStatement) {
|
||||
// if(...) {if(...)} else {...} -- do not unwrap nested `if`
|
||||
return;
|
||||
}
|
||||
new CommentTracker().replaceAndRestoreComments(blockStatement, statement);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExtractFieldInitializerSurrounder extends CodeBlockSurrounder {
|
||||
@@ -397,7 +553,7 @@ public abstract class CodeBlockSurrounder {
|
||||
@NotNull PsiStatement replace(@NotNull Project project, @NotNull PsiElementFactory factory) {
|
||||
myField.normalizeDeclaration();
|
||||
PsiClassInitializer initializer =
|
||||
ObjectUtils.tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(myField), PsiClassInitializer.class);
|
||||
tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(myField), PsiClassInitializer.class);
|
||||
boolean isStatic = myField.hasModifierProperty(PsiModifier.STATIC);
|
||||
if (initializer == null || initializer.hasModifierProperty(PsiModifier.STATIC) != isStatic) {
|
||||
initializer = factory.createClassInitializer();
|
||||
@@ -426,6 +582,27 @@ public abstract class CodeBlockSurrounder {
|
||||
Objects.requireNonNull(myField.getInitializer()).delete();
|
||||
return assignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiExpressionStatement statement = tryCast(anchor, PsiExpressionStatement.class);
|
||||
if (statement == null) return;
|
||||
PsiAssignmentExpression assignment = tryCast(statement.getExpression(), PsiAssignmentExpression.class);
|
||||
if (assignment == null) return;
|
||||
if (myField.hasInitializer()) return;
|
||||
PsiExpression lExpression = assignment.getLExpression();
|
||||
if (!(lExpression instanceof PsiReferenceExpression) || !((PsiReferenceExpression)lExpression).isReferenceTo(myField) ||
|
||||
((PsiReferenceExpression)lExpression).getQualifierExpression() != null) {
|
||||
return;
|
||||
}
|
||||
PsiExpression rExpression = assignment.getRExpression();
|
||||
if (rExpression == null) return;
|
||||
PsiCodeBlock block = tryCast(statement.getParent(), PsiCodeBlock.class);
|
||||
if (block == null || !(block.getParent() instanceof PsiClassInitializer)) return;
|
||||
if (block.getStatementCount() != 1) return;
|
||||
myField.setInitializer(rExpression);
|
||||
block.getParent().delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SplitTrySurrounder extends CodeBlockSurrounder {
|
||||
@@ -533,6 +710,19 @@ public abstract class CodeBlockSurrounder {
|
||||
Objects.requireNonNull(whileStatement.getCondition()).replace(lOperands);
|
||||
return ifStatement;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull PsiWhileStatement anchor(@NotNull PsiStatement context) {
|
||||
return (PsiWhileStatement)context.getParent().getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiWhileStatement whileStatement = tryCast(anchor, PsiWhileStatement.class);
|
||||
if (whileStatement != null) {
|
||||
ConditionalBreakInInfiniteLoopInspection.tryTransform(whileStatement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TernaryToIfSurrounder extends CodeBlockSurrounder {
|
||||
@@ -602,6 +792,21 @@ public abstract class CodeBlockSurrounder {
|
||||
(PsiStatement)((PsiBlockStatement)Objects.requireNonNull(ifStatement.getElseBranch())).getCodeBlock().add(elseStatement);
|
||||
return then ? thenStatement : elseStatement;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull PsiIfStatement anchor(@NotNull PsiStatement context) {
|
||||
// PsiCodeBlock -> PsiBlockStatement -> PsiIfStatement
|
||||
return (PsiIfStatement)context.getParent().getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiIfStatement ifStatement = tryCast(anchor, PsiIfStatement.class);
|
||||
if (ifStatement == null) return;
|
||||
PsiStatement result = collapseIf(ifStatement, "?:");
|
||||
if (result == null) return;
|
||||
myUpstream.collapse(myUpstream.anchor(result));
|
||||
}
|
||||
}
|
||||
|
||||
private static class AndOrToIfSurrounder extends CodeBlockSurrounder {
|
||||
@@ -689,5 +894,32 @@ public abstract class CodeBlockSurrounder {
|
||||
}
|
||||
return rOperands;
|
||||
}
|
||||
|
||||
@Override
|
||||
void collapse(@NotNull PsiElement anchor) {
|
||||
PsiIfStatement ifStatement = tryCast(PsiTreeUtil.skipWhitespacesAndCommentsBackward(anchor), PsiIfStatement.class);
|
||||
if (ifStatement == null) return;
|
||||
PsiStatement result = collapseIf(ifStatement, "&&", "||");
|
||||
if (result == null) return;
|
||||
myUpstream.collapse(myUpstream.anchor(result));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiStatement collapseIf(PsiIfStatement ifStatement, String... operators) {
|
||||
IfConditionalModel model = IfConditionalModel.from(ifStatement, false);
|
||||
if (model == null) return null;
|
||||
ConditionalExpressionGenerator generator = ConditionalExpressionGenerator.from(model);
|
||||
if (generator == null) return null;
|
||||
String operator = generator.getTokenType();
|
||||
if (!ArrayUtil.contains(operator, operators)) return null;
|
||||
CommentTracker commentTracker = new CommentTracker();
|
||||
String conditional = generator.generate(commentTracker);
|
||||
commentTracker.replace(model.getThenExpression(), conditional);
|
||||
PsiStatement branch = model.getElseBranch();
|
||||
if (!PsiTreeUtil.isAncestor(ifStatement, branch, true)) {
|
||||
commentTracker.delete(branch);
|
||||
}
|
||||
return (PsiStatement)commentTracker.replaceAndRestoreComments(ifStatement, model.getThenBranch());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user