mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
SimplifyBooleanExpressionFix: support always-false last disjunct in if
Fixes IDEA-200961 Simplification of "!list.add(type) is always false" may change code semantic
This commit is contained in:
+56
-11
@@ -5,6 +5,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.intention.impl.SplitConditionUtil;
|
||||
import com.intellij.codeInspection.CommonQuickFixBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
|
||||
import com.intellij.openapi.diagnostic.Attachment;
|
||||
@@ -20,6 +21,7 @@ import com.intellij.psi.util.PsiExpressionTrimRenderer;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -64,6 +66,15 @@ public class SimplifyBooleanExpressionFix extends LocalQuickFixOnPsiElement {
|
||||
if (!mySubExpressionValue) {
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(subExpression.getParent());
|
||||
if (parent instanceof PsiWhileStatement || parent instanceof PsiForStatement) return true;
|
||||
// code like "if (foo || alwaysFalseWithSideEffects) {}"
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression polyadic = (PsiPolyadicExpression)parent;
|
||||
if (polyadic.getOperationTokenType().equals(JavaTokenType.OROR)
|
||||
&& PsiTreeUtil.isAncestor(ArrayUtil.getLastElement(polyadic.getOperands()), subExpression, false)
|
||||
&& PsiUtil.skipParenthesizedExprUp(parent.getParent()) instanceof PsiIfStatement) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -125,17 +136,7 @@ public class SimplifyBooleanExpressionFix extends LocalQuickFixOnPsiElement {
|
||||
if (subExpression == null) return;
|
||||
CommentTracker ct = new CommentTracker();
|
||||
if (shouldExtractSideEffect()) {
|
||||
if (!mySubExpressionValue) {
|
||||
// Prevent extracting while condition to internal 'if'
|
||||
PsiWhileStatement whileStatement = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprUp(subExpression.getParent()), PsiWhileStatement.class);
|
||||
if (whileStatement != null && whileStatement.getCondition() != null) {
|
||||
PsiStatement replacement = JavaPsiFacade.getElementFactory(project)
|
||||
.createStatementFromText("if(" + whileStatement.getCondition().getText() + ");", whileStatement);
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)whileStatement.replace(replacement);
|
||||
subExpression = Objects.requireNonNull(ifStatement.getCondition());
|
||||
}
|
||||
}
|
||||
subExpression = RefactoringUtil.ensureCodeBlock(subExpression);
|
||||
subExpression = ensureCodeBlock(project, subExpression);
|
||||
if (subExpression == null) {
|
||||
LOG.error("ensureCodeBlock returned null", new Attachment("subExpression.txt", getSubExpression().getText()));
|
||||
return;
|
||||
@@ -163,6 +164,50 @@ public class SimplifyBooleanExpressionFix extends LocalQuickFixOnPsiElement {
|
||||
simplifyExpression(expression);
|
||||
}
|
||||
|
||||
public PsiExpression ensureCodeBlock(@NotNull Project project, PsiExpression subExpression) {
|
||||
if (!mySubExpressionValue) {
|
||||
// Prevent extracting while condition to internal 'if'
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(subExpression.getParent());
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
if (parent instanceof PsiWhileStatement) {
|
||||
PsiWhileStatement whileStatement = (PsiWhileStatement)parent;
|
||||
if (whileStatement.getCondition() != null) {
|
||||
PsiStatement replacement =
|
||||
factory.createStatementFromText("if(" + whileStatement.getCondition().getText() + ");", whileStatement);
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)whileStatement.replace(replacement);
|
||||
subExpression = Objects.requireNonNull(ifStatement.getCondition());
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent;
|
||||
if (JavaTokenType.OROR.equals(polyadicExpression.getOperationTokenType())) {
|
||||
PsiExpression expression = expandLastIfDisjunct(polyadicExpression, subExpression, factory);
|
||||
if (expression != null) {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return RefactoringUtil.ensureCodeBlock(subExpression);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression expandLastIfDisjunct(PsiPolyadicExpression orChain,
|
||||
PsiExpression subExpression,
|
||||
PsiElementFactory factory) {
|
||||
PsiIfStatement ifStatement = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprUp(orChain.getParent()), PsiIfStatement.class);
|
||||
if (ifStatement == null) return null;
|
||||
PsiExpression lastOperand = ArrayUtil.getLastElement(orChain.getOperands());
|
||||
if (!PsiTreeUtil.isAncestor(lastOperand, subExpression, false)) return null;
|
||||
orChain.replace(SplitConditionUtil.getLOperands(orChain, orChain.getTokenBeforeOperand(lastOperand)));
|
||||
ControlFlowUtils.ensureElseBranch(ifStatement);
|
||||
PsiBlockStatement elseBranch = (PsiBlockStatement)Objects.requireNonNull(ifStatement.getElseBranch());
|
||||
PsiCodeBlock codeBlock = elseBranch.getCodeBlock();
|
||||
PsiStatement replacement = factory.createStatementFromText("if(" + subExpression.getText() + ");", ifStatement);
|
||||
PsiIfStatement alwaysFalseIf = (PsiIfStatement)codeBlock.addAfter(replacement, codeBlock.getLBrace());
|
||||
return Objects.requireNonNull(alwaysFalseIf.getCondition());
|
||||
}
|
||||
|
||||
private static boolean simplifyIfOrLoopStatement(final PsiExpression expression) throws IncorrectOperationException {
|
||||
boolean condition = Boolean.parseBoolean(expression.getText());
|
||||
if (!(expression instanceof PsiLiteralExpression) || !PsiType.BOOLEAN.equals(expression.getType())) return false;
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class Mutant {
|
||||
List<String> types = new ArrayList<>();
|
||||
|
||||
void consider(String type, boolean unmodifiable) {
|
||||
if (unmodifiable) {
|
||||
if(Math.random() > 0.5) {
|
||||
System.out.println("1");
|
||||
}
|
||||
} else {
|
||||
types.add(type);
|
||||
System.out.println("2");
|
||||
}
|
||||
if (unmodifiable) {
|
||||
if(Math.random() > 0.5) {
|
||||
System.out.println("1");
|
||||
}
|
||||
} else {
|
||||
types.add(type);
|
||||
}
|
||||
if (unmodifiable) {
|
||||
if(Math.random() > 0.5) {
|
||||
System.out.println("1");
|
||||
}
|
||||
} else {
|
||||
types.add(type);
|
||||
}
|
||||
if (unmodifiable)
|
||||
System.out.println("1");
|
||||
else {
|
||||
types.add(type);
|
||||
System.out.println("2");
|
||||
}
|
||||
// Cannot extract side effect in the middle
|
||||
if (unmodifiable || Math.random() > 0.5)
|
||||
System.out.println("1");
|
||||
else
|
||||
System.out.println("2");
|
||||
types.add(type);
|
||||
if (unmodifiable)
|
||||
System.out.println("1");
|
||||
else
|
||||
System.out.println("2");
|
||||
|
||||
System.out.println("types = " + types);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Mutant().consider("A", false);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// "Fix all 'Constant conditions & exceptions' problems in file" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class Mutant {
|
||||
List<String> types = new ArrayList<>();
|
||||
|
||||
void consider(String type, boolean unmodifiable) {
|
||||
if (unmodifiable || !types.<caret>add(type)) {
|
||||
if(Math.random() > 0.5) {
|
||||
System.out.println("1");
|
||||
}
|
||||
} else {
|
||||
System.out.println("2");
|
||||
}
|
||||
if (unmodifiable || !types.add(type)) {
|
||||
if(Math.random() > 0.5) {
|
||||
System.out.println("1");
|
||||
}
|
||||
}
|
||||
if (unmodifiable || !types.add(type))
|
||||
if(Math.random() > 0.5) {
|
||||
System.out.println("1");
|
||||
}
|
||||
if (unmodifiable || !types.add(type))
|
||||
System.out.println("1");
|
||||
else
|
||||
System.out.println("2");
|
||||
// Cannot extract side effect in the middle
|
||||
if (unmodifiable || !types.add(type) || Math.random() > 0.5)
|
||||
System.out.println("1");
|
||||
else
|
||||
System.out.println("2");
|
||||
if (!types.add(type) || unmodifiable)
|
||||
System.out.println("1");
|
||||
else
|
||||
System.out.println("2");
|
||||
|
||||
System.out.println("types = " + types);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Mutant().consider("A", false);
|
||||
}
|
||||
}
|
||||
+8
@@ -19,6 +19,8 @@ package com.intellij.java.codeInsight.daemon.quickFix;
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class UnwrapIfStatementFixTest extends LightQuickFixParameterizedTestCase {
|
||||
@@ -28,6 +30,12 @@ public class UnwrapIfStatementFixTest extends LightQuickFixParameterizedTestCase
|
||||
return new LocalInspectionTool[]{new DataFlowInspection()};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return LightCodeInsightFixtureTestCase.JAVA_10_ANNOTATED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/unwrapIfStatement";
|
||||
|
||||
+23
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.siyeh.ig.psiutils;
|
||||
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
@@ -1069,6 +1070,28 @@ public class ControlFlowUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the {@code if} statement has the {@code else} branch which is a block statement (adding it if absent)
|
||||
* @param ifStatement an {@code if} statement to add an else branch or expand it to the block
|
||||
*/
|
||||
public static void ensureElseBranch(PsiIfStatement ifStatement) {
|
||||
PsiStatement elseBranch = ifStatement.getElseBranch();
|
||||
if (elseBranch != null) {
|
||||
if (!(elseBranch instanceof PsiBlockStatement)) {
|
||||
BlockUtils.expandSingleStatementToBlockStatement(elseBranch);
|
||||
}
|
||||
} else {
|
||||
PsiStatement thenBranch = ifStatement.getThenBranch();
|
||||
PsiBlockStatement emptyBlock = BlockUtils.createBlockStatement(ifStatement.getProject());
|
||||
if (thenBranch == null) {
|
||||
ifStatement.setThenBranch(emptyBlock);
|
||||
} else if (!(thenBranch instanceof PsiBlockStatement)) {
|
||||
BlockUtils.expandSingleStatementToBlockStatement(thenBranch);
|
||||
}
|
||||
ifStatement.setElseBranch(emptyBlock);
|
||||
}
|
||||
}
|
||||
|
||||
public enum InitializerUsageStatus {
|
||||
// Variable is declared just before the wanted place
|
||||
DECLARED_JUST_BEFORE,
|
||||
|
||||
Reference in New Issue
Block a user