mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
IDEA-199811 RefactoringUtil#ensureCodeBlock: support while conditions; support && chains in if-then and while
This commit is contained in:
@@ -15,11 +15,13 @@
|
||||
*/
|
||||
package com.intellij.refactoring.util;
|
||||
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.ExpectedTypeInfo;
|
||||
import com.intellij.codeInsight.ExpectedTypesProvider;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.codeInsight.intention.impl.SplitConditionUtil;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -48,6 +50,7 @@ import com.intellij.psi.util.*;
|
||||
import com.intellij.refactoring.PackageWrapper;
|
||||
import com.intellij.refactoring.introduceField.ElementToWorkOn;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.text.UniqueNameGenerator;
|
||||
@@ -1028,8 +1031,19 @@ public class RefactoringUtil {
|
||||
parent = PsiTreeUtil.getParentOfType(expression, PsiField.class, true, PsiClass.class);
|
||||
if (parent == null) return null;
|
||||
}
|
||||
|
||||
PsiPolyadicExpression andChain = findSurroundingConjunction(expression);
|
||||
PsiExpression operand = null;
|
||||
if (andChain != null) {
|
||||
operand = StreamEx.of(andChain.getOperands()).findFirst(op -> PsiTreeUtil.isAncestor(op, expression, false))
|
||||
.orElseThrow(AssertionError::new);
|
||||
}
|
||||
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (parent instanceof PsiStatement && grandParent instanceof PsiCodeBlock) {
|
||||
if (andChain == null &&
|
||||
parent instanceof PsiStatement &&
|
||||
!(parent instanceof PsiWhileStatement) &&
|
||||
grandParent instanceof PsiCodeBlock) {
|
||||
if (!(parent instanceof PsiForStatement) ||
|
||||
!PsiTreeUtil.isAncestor(((PsiForStatement)parent).getInitialization(), expression, true) ||
|
||||
!hasNameCollision(((PsiForStatement)parent).getInitialization(), grandParent)) {
|
||||
@@ -1083,6 +1097,12 @@ public class RefactoringUtil {
|
||||
rExpression.replace(fieldInitializer);
|
||||
Objects.requireNonNull(field.getInitializer()).delete();
|
||||
newParent = assignment;
|
||||
}
|
||||
else if (parent instanceof PsiIfStatement && andChain != null) {
|
||||
newParent = splitIf((PsiIfStatement)parent, andChain, operand);
|
||||
}
|
||||
else if (parent instanceof PsiWhileStatement) {
|
||||
newParent = extractWhileCondition((PsiWhileStatement)parent, andChain, operand);
|
||||
} else {
|
||||
PsiBlockStatement blockStatement = (PsiBlockStatement)parent.replace(factory.createStatementFromText("{}", parent));
|
||||
newParent = blockStatement.getCodeBlock().add(copy);
|
||||
@@ -1091,6 +1111,86 @@ public class RefactoringUtil {
|
||||
return (T)PsiTreeUtil.releaseMark(newParent, marker);
|
||||
}
|
||||
|
||||
private static PsiElement extractWhileCondition(PsiWhileStatement whileStatement, PsiPolyadicExpression andChain, PsiExpression operand) {
|
||||
PsiExpression oldCondition = Objects.requireNonNull(whileStatement.getCondition());
|
||||
PsiStatement body = whileStatement.getBody();
|
||||
PsiBlockStatement blockBody;
|
||||
Project project = whileStatement.getProject();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
if (body == null) {
|
||||
PsiWhileStatement newWhileStatement = (PsiWhileStatement)factory.createStatementFromText("while(true) {}", whileStatement);
|
||||
blockBody = (PsiBlockStatement)Objects.requireNonNull(newWhileStatement.getBody());
|
||||
whileStatement = (PsiWhileStatement)whileStatement.replace(newWhileStatement);
|
||||
}
|
||||
else if (body instanceof PsiBlockStatement) {
|
||||
blockBody = (PsiBlockStatement)body;
|
||||
}
|
||||
else {
|
||||
PsiBlockStatement newBody = BlockUtils.createBlockStatement(project);
|
||||
newBody.add(body);
|
||||
blockBody = (PsiBlockStatement)body.replace(newBody);
|
||||
}
|
||||
PsiExpression lOperands;
|
||||
PsiExpression rOperands;
|
||||
if (andChain != null) {
|
||||
lOperands = SplitConditionUtil.getLOperands(andChain, andChain.getTokenBeforeOperand(operand));
|
||||
rOperands = getRightOperands(andChain, operand);
|
||||
}
|
||||
else {
|
||||
lOperands = factory.createExpressionFromText("true", whileStatement);
|
||||
rOperands = oldCondition;
|
||||
}
|
||||
PsiCodeBlock codeBlock = blockBody.getCodeBlock();
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)factory.createStatementFromText("if(!true) break;", whileStatement);
|
||||
ifStatement = (PsiIfStatement)codeBlock.addAfter(ifStatement, codeBlock.getLBrace());
|
||||
PsiPrefixExpression negation = (PsiPrefixExpression)Objects.requireNonNull(ifStatement.getCondition());
|
||||
PsiElement newParent = Objects.requireNonNull(negation.getOperand()).replace(rOperands);
|
||||
Objects.requireNonNull(whileStatement.getCondition()).replace(lOperands);
|
||||
return newParent;
|
||||
}
|
||||
|
||||
private static PsiElement splitIf(PsiIfStatement outerIf, PsiPolyadicExpression andChain, PsiExpression operand) {
|
||||
PsiExpression lOperands = SplitConditionUtil.getLOperands(andChain, andChain.getTokenBeforeOperand(operand));
|
||||
PsiExpression rOperands = getRightOperands(andChain, operand);
|
||||
Project project = outerIf.getProject();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiBlockStatement newThenBranch = (PsiBlockStatement)factory.createStatementFromText("{if(true);}", outerIf);
|
||||
PsiStatement thenBranch = Objects.requireNonNull(outerIf.getThenBranch());
|
||||
Objects.requireNonNull(((PsiIfStatement)newThenBranch.getCodeBlock().getStatements()[0]).getThenBranch()).replace(thenBranch);
|
||||
newThenBranch = (PsiBlockStatement)thenBranch.replace(newThenBranch);
|
||||
PsiIfStatement innerIf =
|
||||
(PsiIfStatement)CodeStyleManager.getInstance(project).reformat(newThenBranch.getCodeBlock().getStatements()[0]);
|
||||
PsiElement newParent = Objects.requireNonNull(innerIf.getCondition()).replace(rOperands);
|
||||
andChain.replace(lOperands);
|
||||
return newParent;
|
||||
}
|
||||
|
||||
private static PsiExpression getRightOperands(PsiPolyadicExpression andChain, PsiExpression operand) {
|
||||
PsiExpression rOperands;
|
||||
if (operand == ArrayUtil.getLastElement(andChain.getOperands())) {
|
||||
rOperands = operand;
|
||||
}
|
||||
else {
|
||||
rOperands = SplitConditionUtil.getROperands(andChain, andChain.getTokenBeforeOperand(operand));
|
||||
// To preserve mark
|
||||
((PsiPolyadicExpression)rOperands).getOperands()[0].replace(operand);
|
||||
}
|
||||
return rOperands;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiPolyadicExpression findSurroundingConjunction(@NotNull PsiExpression expression) {
|
||||
PsiExpression current = expression;
|
||||
PsiPolyadicExpression andChain;
|
||||
do {
|
||||
current = andChain = PsiTreeUtil.getParentOfType(current, PsiPolyadicExpression.class, true,
|
||||
PsiStatement.class, PsiLambdaExpression.class);
|
||||
}
|
||||
while (andChain != null && (andChain.getOperationTokenType() != JavaTokenType.ANDAND ||
|
||||
PsiTreeUtil.isAncestor(andChain.getOperands()[0], expression, false)));
|
||||
return andChain;
|
||||
}
|
||||
|
||||
private static boolean hasNameCollision(PsiElement declaration, PsiElement context) {
|
||||
if (declaration instanceof PsiDeclarationStatement) {
|
||||
PsiResolveHelper helper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper();
|
||||
|
||||
@@ -1,18 +1,45 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static void test(List<String> list) {
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
for (String s : list) {
|
||||
if (s != null) {
|
||||
if (s.startsWith("x")) {
|
||||
System.out.println("Ok!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(list.size() > 2) {
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
System.out.println("Ok!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(list.size() > 2) {
|
||||
boolean b = false;
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
b = list.size() < 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b) {
|
||||
System.out.println("Ok!");
|
||||
}
|
||||
}
|
||||
if(list.size() > 2 || list.stream().filter(x -> x != null).anyMatch(x -> x.startsWith("x"))) { // not supported
|
||||
System.out.println("Ok!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static void test(List<String> list) {
|
||||
while(true) {
|
||||
boolean b = true;
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b) break;
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test2(List<String> list) {
|
||||
while(list.size() > 2) {
|
||||
boolean b = true;
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b) break;
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test3(List<String> list) {
|
||||
while(true) {
|
||||
boolean b = false;
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
b = list.size() > 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(b)) break;
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test4(List<String> list) {
|
||||
while(list.size() > 2) {
|
||||
long count = 0L;
|
||||
for (String x : list) {
|
||||
if (x != null) {
|
||||
if (x.startsWith("x")) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(count > 10 && list.size() < 10)) break;
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
native List<String> process(List<String> list);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -8,6 +8,15 @@ public class Main {
|
||||
if(list.stream().filter(x -> x != null).an<caret>yMatch(x -> x.startsWith("x"))) {
|
||||
System.out.println("Ok!");
|
||||
}
|
||||
if(list.size() > 2 && list.stream().filter(x -> x != null).anyMatch(x -> x.startsWith("x"))) {
|
||||
System.out.println("Ok!");
|
||||
}
|
||||
if(list.size() > 2 && list.stream().filter(x -> x != null).anyMatch(x -> x.startsWith("x")) && list.size() < 10) {
|
||||
System.out.println("Ok!");
|
||||
}
|
||||
if(list.size() > 2 || list.stream().filter(x -> x != null).anyMatch(x -> x.startsWith("x"))) { // not supported
|
||||
System.out.println("Ok!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static void test(List<String> list) {
|
||||
while(list.stream().filter(x -> x != null).an<caret>yMatch(x -> x.startsWith("x"))) {
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test2(List<String> list) {
|
||||
while(list.size() > 2 && list.stream().filter(x -> x != null).anyMatch(x -> x.startsWith("x"))) {
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test3(List<String> list) {
|
||||
while(list.stream().filter(x -> x != null).anyMatch(x -> x.startsWith("x")) && list.size() > 2) {
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test4(List<String> list) {
|
||||
while(list.size() > 2 && list.stream().filter(x -> x != null).filter(x -> x.startsWith("x")).count() > 10 && list.size() < 10) {
|
||||
list = process(list);
|
||||
}
|
||||
}
|
||||
|
||||
native List<String> process(List<String> list);
|
||||
}
|
||||
@@ -922,7 +922,14 @@ public class ControlFlowUtils {
|
||||
if (checkExecuted && parent instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent;
|
||||
IElementType type = polyadicExpression.getOperationTokenType();
|
||||
if ((type.equals(JavaTokenType.ANDAND) || type.equals(JavaTokenType.OROR)) && polyadicExpression.getOperands()[0] != cur) {
|
||||
if (type.equals(JavaTokenType.ANDAND) && polyadicExpression.getOperands()[0] != cur) {
|
||||
PsiElement polyParent = PsiUtil.skipParenthesizedExprUp(polyadicExpression.getParent());
|
||||
// not the first in the &&/|| chain: we cannot properly generate code which would short-circuit as well
|
||||
// except some special cases
|
||||
return (polyParent instanceof PsiIfStatement && ((PsiIfStatement)polyParent).getElseBranch() == null) ||
|
||||
(polyParent instanceof PsiWhileStatement);
|
||||
}
|
||||
else if (type.equals(JavaTokenType.OROR) && polyadicExpression.getOperands()[0] != cur) {
|
||||
// not the first in the &&/|| chain: we cannot properly generate code which would short-circuit as well
|
||||
return false;
|
||||
}
|
||||
@@ -945,6 +952,7 @@ public class ControlFlowUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiWhileStatement && ((PsiWhileStatement)parent).getCondition() == cur) return true;
|
||||
if(parent instanceof PsiReturnStatement || parent instanceof PsiExpressionStatement) return true;
|
||||
if(parent instanceof PsiLocalVariable) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
|
||||
Reference in New Issue
Block a user