IDEA-85926 ('Compute constant value' intention shouldn't be applied in right-associative manner)

and fix the intentions in the presence of polyadic expressions
This commit is contained in:
Bas Leijdekkers
2012-05-23 21:48:48 +02:00
parent b7e97699c5
commit 9886c29ead
6 changed files with 216 additions and 206 deletions
@@ -37,7 +37,6 @@ replace.switch.with.if.intention.name=Replace 'switch' with 'if'
replace.switch.with.if.intention.family.name=Replace Switch with If
simplify.variable.intention.name=Replace with Java-style array declaration
simplify.variable.intention.family.name=Replace with Java Style Array Declaration
constant.expression.intention.name=Compute constant value
constant.expression.intention.family.name=Compute Constant Value
create.assert.intention.name=Create JUnit Assertion
create.assert.intention.family.name=Create JUnit Assertion
@@ -176,7 +175,7 @@ add.braces.intention.name=Add Braces to ''{0}'' statement
remove.braces.intention.name=Remove braces from ''{0}'' statement
flip.commutative.method.call.intention.name=Flip ''.{0}()''
flip.commutative.method.call.intention.name1=Flip ''.{0}()'' (may change semantics)
constant.subexpression.intention.name=Compute constant value of ''{0}''
constant.expression.intention.name=Compute constant value of ''{0}''
status.bar.escape.highlighting.message=Press Escape to remove the highlighting
1.fully.qualified.name.status.bar.escape.highlighting.message=1 fully qualified name replaced with import (press Escape to remove highlighting)
multiple.fully.qualified.names.status.bar.escape.highlighting.message={0} fully qualified names replaced with import (press Escape to remove highlighting)
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,15 +16,24 @@
package com.siyeh.ipp.constant;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ipp.base.Intention;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.base.MutablyNamedIntention;
import com.siyeh.ipp.base.PsiElementPredicate;
import com.siyeh.ipp.psiutils.ExpressionUtils;
import com.siyeh.ipp.psiutils.HighlightUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class ConstantExpressionIntention extends Intention {
public class ConstantExpressionIntention extends MutablyNamedIntention {
@Override
protected String getTextForElement(PsiElement element) {
final String text = HighlightUtil.getPresentableText(element);
return IntentionPowerPackBundle.message("constant.expression.intention.name", text);
}
@Override
@NotNull
@@ -33,22 +42,16 @@ public class ConstantExpressionIntention extends Intention {
}
@Override
public void processIntention(PsiElement element)
throws IncorrectOperationException {
final PsiExpression expression =
(PsiExpression)element;
final Object value =
ExpressionUtils.computeConstantExpression(expression);
public void processIntention(PsiElement element) throws IncorrectOperationException {
final PsiExpression expression = (PsiExpression)element;
final Object value = ExpressionUtils.computeConstantExpression(expression);
@NonNls final String newExpression;
if (value instanceof String) {
final String string = (String)value;
newExpression =
'"' + StringUtil.escapeStringCharacters(string) + '"';
newExpression = '"' + StringUtil.escapeStringCharacters(string) + '"';
}
else if (value instanceof Character) {
newExpression =
'\'' + StringUtil.escapeStringCharacters(value.toString()) +
'\'';
newExpression = '\'' + StringUtil.escapeStringCharacters(value.toString()) + '\'';
}
else if (value instanceof Long) {
newExpression = value.toString() + 'L';
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,28 +24,34 @@ import com.siyeh.ipp.psiutils.ExpressionUtils;
class ConstantExpressionPredicate implements PsiElementPredicate {
public boolean satisfiedBy(PsiElement element) {
if (!(element instanceof PsiBinaryExpression)) {
if (!(element instanceof PsiPolyadicExpression)) {
return false;
}
if (element instanceof PsiLiteralExpression ||
element instanceof PsiClassObjectAccessExpression) {
if (element instanceof PsiLiteralExpression || element instanceof PsiClassObjectAccessExpression) {
return false;
}
final PsiBinaryExpression expression = (PsiBinaryExpression)element;
final PsiExpression rhs = expression.getROperand();
if (rhs == null) {
final PsiPolyadicExpression expression = (PsiPolyadicExpression)element;
final PsiType expressionType = expression.getType();
if (expressionType.equalsToText("java.lang.String")) {
// intention disabled for string concatenations because of performance issues on
// relatively common large string expressions.
return false;
}
final PsiType type = rhs.getType();
if (type == null || type.equalsToText("java.lang.String")) {
return false;
final PsiExpression[] operands = expression.getOperands();
for (PsiExpression operand : operands) {
if (operand == null) {
return false;
}
final PsiType type = operand.getType();
if (type == null || type.equalsToText("java.lang.String")) {
return false;
}
}
if (!PsiUtil.isConstantExpression(expression)) {
return false;
}
try {
final Object value =
ExpressionUtils.computeConstantExpression(expression, true);
final Object value = ExpressionUtils.computeConstantExpression(expression, true);
if (value == null) {
return false;
}
@@ -54,7 +60,6 @@ class ConstantExpressionPredicate implements PsiElementPredicate {
return false;
}
final PsiElement parent = element.getParent();
return !(parent instanceof PsiExpression &&
PsiUtil.isConstantExpression((PsiExpression)parent));
return !(parent instanceof PsiExpression) || !PsiUtil.isConstantExpression((PsiExpression)parent);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,17 +15,13 @@
*/
package com.siyeh.ipp.constant;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiBinaryExpression;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.base.MutablyNamedIntention;
import com.siyeh.ipp.base.PsiElementPredicate;
import com.siyeh.ipp.psiutils.ConcatenationUtils;
import com.siyeh.ipp.psiutils.ExpressionUtils;
import com.siyeh.ipp.psiutils.HighlightUtil;
import org.jetbrains.annotations.NotNull;
public class ConstantSubexpressionIntention extends MutablyNamedIntention {
@@ -38,108 +34,109 @@ public class ConstantSubexpressionIntention extends MutablyNamedIntention {
@Override
protected String getTextForElement(PsiElement element) {
final PsiBinaryExpression binaryExpression =
(PsiBinaryExpression)element.getParent();
assert binaryExpression != null;
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression leftSide;
if (lhs instanceof PsiBinaryExpression) {
final PsiBinaryExpression lhsBinaryExpression =
(PsiBinaryExpression)lhs;
leftSide = lhsBinaryExpression.getROperand();
final PsiJavaToken token;
if (element instanceof PsiJavaToken) {
token = (PsiJavaToken)element;
} else {
final PsiElement prevSibling = element.getPrevSibling();
if (prevSibling instanceof PsiJavaToken) {
token = (PsiJavaToken)prevSibling;
} else {
throw new AssertionError();
}
}
else {
leftSide = lhs;
}
final PsiJavaToken operationSign = binaryExpression.getOperationSign();
final PsiExpression rhs = binaryExpression.getROperand();
assert rhs != null;
assert leftSide != null;
return IntentionPowerPackBundle.message(
"constant.subexpression.intention.name", leftSide.getText() +
' ' + operationSign.getText() + ' ' + rhs.getText());
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)element.getParent();
final PsiPolyadicExpression subexpression = ConstantSubexpressionPredicate.getSubexpression(polyadicExpression, token);
final String text = HighlightUtil.getPresentableText(subexpression);
return IntentionPowerPackBundle.message("constant.expression.intention.name", text);
}
@Override
public void processIntention(@NotNull PsiElement element)
throws IncorrectOperationException {
final PsiExpression expression = (PsiExpression)element.getParent();
assert expression != null;
String newExpression = "";
final Object constantValue;
if (expression instanceof PsiBinaryExpression) {
final PsiBinaryExpression copy =
(PsiBinaryExpression)expression.copy();
final PsiExpression lhs = copy.getLOperand();
if (lhs instanceof PsiBinaryExpression) {
final PsiBinaryExpression lhsBinaryExpression =
(PsiBinaryExpression)lhs;
newExpression += getLeftSideText(lhsBinaryExpression);
final PsiExpression rightSide =
lhsBinaryExpression.getROperand();
assert rightSide != null;
lhs.replace(rightSide);
}
if (ConcatenationUtils.isConcatenation(expression)) {
constantValue = computeConstantStringExpression(copy);
}
else {
constantValue =
ExpressionUtils.computeConstantExpression(copy);
public void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
final PsiJavaToken token;
if (element instanceof PsiJavaToken) {
token = (PsiJavaToken)element;
} else {
final PsiElement prevSibling = element.getPrevSibling();
if (prevSibling instanceof PsiJavaToken) {
token = (PsiJavaToken)prevSibling;
} else {
throw new AssertionError();
}
}
else {
constantValue =
ExpressionUtils.computeConstantExpression(expression);
}
if (constantValue instanceof String) {
newExpression += '"' + StringUtil.escapeStringCharacters(
constantValue.toString()) + '"';
}
else if (constantValue != null) {
if (constantValue instanceof Number) {
final Number number = (Number)constantValue;
if (0 > number.doubleValue()) {
newExpression += " ";
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)element.getParent();
final PsiPolyadicExpression subexpression = ConstantSubexpressionPredicate.getSubexpression(polyadicExpression, token);
final Object value = ExpressionUtils.computeConstantExpression(subexpression);
final StringBuilder newExpressionText = new StringBuilder();
final PsiExpression[] operands = polyadicExpression.getOperands();
PsiExpression prevOperand = null;
PsiJavaToken prevToken = null;
for (PsiExpression operand : operands) {
final PsiJavaToken currentToken = polyadicExpression.getTokenBeforeOperand(operand);
if (token == currentToken) {
if (prevToken != null) {
newExpressionText.append(prevToken.getText());
}
if (newExpressionText.length() > 0) {
newExpressionText.append(' ');
}
if (value instanceof Long) {
newExpressionText.append(value).append('L');
} else if (value instanceof Double) {
final double v = ((Double)value).doubleValue();
if (Double.isNaN(v)) {
newExpressionText.append("java.lang.Double.NaN");
}
else if (Double.isInfinite(v)) {
if (v > 0.0) {
newExpressionText.append("java.lang.Double.POSITIVE_INFINITY");
}
else {
newExpressionText.append("java.lang.Double.NEGATIVE_INFINITY");
}
}
else {
newExpressionText.append(Double.toString(v));
}
}
else if (value instanceof Float) {
final float v = ((Float)value).floatValue();
if (Float.isNaN(v)) {
newExpressionText.append("java.lang.Float.NaN");
}
else if (Float.isInfinite(v)) {
if (v > 0.0F) {
newExpressionText.append("java.lang.Float.POSITIVE_INFINITY");
}
else {
newExpressionText.append("java.lang.Float.NEGATIVE_INFINITY");
}
}
else {
newExpressionText.append(Float.toString(v)).append('f');
}
} else {
newExpressionText.append(value);
}
prevOperand = null;
prevToken = null;
} else {
if (prevToken != null) {
newExpressionText.append(prevToken.getText());
}
if (prevOperand != null) {
newExpressionText.append(prevOperand.getText());
}
prevOperand = operand;
prevToken = currentToken;
}
newExpression += constantValue.toString();
}
replaceExpression(newExpression, expression);
}
/**
* handles the specified expression as if it was part of a string expression
* (even if it's of another type) and computes a constant string expression
* from it.
*/
private static String computeConstantStringExpression(
PsiBinaryExpression expression) {
final PsiExpression lhs = expression.getLOperand();
final String lhsText = lhs.getText();
String result;
if (lhsText.charAt(0) == '\'' || lhsText.charAt(0) == '"') {
result = lhsText.substring(1, lhsText.length() - 1);
if (prevToken != null) {
newExpressionText.append(prevToken.getText());
}
else {
result = lhsText;
if (prevOperand != null) {
newExpressionText.append(prevOperand.getText());
}
final PsiExpression rhs = expression.getROperand();
assert rhs != null;
final String rhsText = rhs.getText();
if (rhsText.charAt(0) == '\'' || rhsText.charAt(0) == '"') {
result += rhsText.substring(1, rhsText.length() - 1);
}
else {
result += rhsText;
}
return result;
}
private static String getLeftSideText(
PsiBinaryExpression binaryExpression) {
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiJavaToken sign = binaryExpression.getOperationSign();
return lhs.getText() + sign.getText();
replaceExpression(newExpressionText.toString(), polyadicExpression);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,47 +19,71 @@ import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ipp.base.PsiElementPredicate;
import com.siyeh.ipp.psiutils.ExpressionUtils;
import org.jetbrains.annotations.Nullable;
class ConstantSubexpressionPredicate implements PsiElementPredicate {
public boolean satisfiedBy(PsiElement element) {
if (!(element instanceof PsiJavaToken) &&
!(element.getPrevSibling() instanceof PsiJavaToken)) {
return false;
final PsiJavaToken token;
if (element instanceof PsiJavaToken) {
token = (PsiJavaToken)element;
}
else {
final PsiElement prevSibling = element.getPrevSibling();
if (prevSibling instanceof PsiJavaToken) {
token = (PsiJavaToken)prevSibling;
}
else {
return false;
}
}
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiBinaryExpression)) {
if (!(parent instanceof PsiPolyadicExpression)) {
return false;
}
final PsiBinaryExpression binaryExpression =
(PsiBinaryExpression)parent;
final PsiType type = binaryExpression.getType();
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent;
final PsiType type = polyadicExpression.getType();
if (type == null || type.equalsToText("java.lang.String")) {
// handled by JoinConcatenatedStringLiteralsIntention
return false;
}
final PsiBinaryExpression subexpression =
getSubexpression(binaryExpression);
final PsiPolyadicExpression subexpression = getSubexpression(polyadicExpression, token);
if (subexpression == null) {
return false;
}
if (binaryExpression.equals(subexpression) &&
!isPartOfConstantExpression(binaryExpression)) {
// handled by ConstantExpressonIntention
if (!isPartOfLargerExpression(polyadicExpression)) {
// handled by ConstantExpressionIntention
return false;
}
if (!PsiUtil.isConstantExpression(subexpression)) {
return false;
}
final Object value =
ExpressionUtils.computeConstantExpression(subexpression);
final Object value = ExpressionUtils.computeConstantExpression(subexpression);
return value != null;
}
private static boolean isPartOfConstantExpression(
PsiBinaryExpression binaryExpression) {
final PsiElement containingElement = binaryExpression.getParent();
static PsiPolyadicExpression getSubexpression(PsiPolyadicExpression expression, PsiJavaToken token) {
final PsiExpression[] operands = expression.getOperands();
if (operands.length == 2) {
return expression;
}
for (int i = 1; i < operands.length; i++) {
final PsiExpression operand = operands[i];
final PsiJavaToken currentToken = expression.getTokenBeforeOperand(operand);
if (currentToken == token) {
final String binaryExpressionText = operands[i - 1].getText() + ' ' + token.getText() + ' ' + operand.getText();
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject());
return (PsiBinaryExpression)factory.createExpressionFromText(binaryExpressionText, expression);
}
}
return null;
}
private static boolean isPartOfLargerExpression(PsiPolyadicExpression expression) {
if (expression.getOperands().length > 2) {
return true;
}
final PsiElement containingElement = expression.getParent();
if (containingElement instanceof PsiExpression) {
final PsiExpression containingExpression =
(PsiExpression)containingElement;
@@ -72,38 +96,4 @@ class ConstantSubexpressionPredicate implements PsiElementPredicate {
}
return true;
}
/**
* Returns the smallest subexpression (if precendence allows it). example:
* variable + 2 + 3 normally gets evaluated left to right -> (variable + 2)
* + 3 this method returns the right most legal subexpression -> 2 + 3
*/
@Nullable
private static PsiBinaryExpression getSubexpression(
PsiBinaryExpression expression) {
final PsiExpression rhs = expression.getROperand();
if (rhs == null) {
return null;
}
final PsiExpression lhs = expression.getLOperand();
if (!(lhs instanceof PsiBinaryExpression)) {
return expression;
}
final PsiBinaryExpression lhsBinaryExpression =
(PsiBinaryExpression)lhs;
final PsiExpression leftSide = lhsBinaryExpression.getROperand();
if (leftSide == null) {
return null;
}
try {
final PsiBinaryExpression binaryExpression =
(PsiBinaryExpression)expression.copy();
final PsiExpression lOperand = binaryExpression.getLOperand();
lOperand.replace(leftSide);
return binaryExpression;
}
catch (Throwable ignore) {
return null;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2007-2011 Bas Leijdekkers
* Copyright 2007-2012 Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,8 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@@ -51,32 +52,24 @@ public class HighlightUtil {
final Application application = ApplicationManager.getApplication();
application.invokeLater(new Runnable() {
public void run() {
final PsiElement[] elements =
PsiUtilBase.toPsiElementArray(elementCollection);
final PsiElement[] elements = PsiUtilCore.toPsiElementArray(elementCollection);
final PsiElement firstElement = elements[0];
if (!firstElement.isValid()) {
return;
}
final Project project = firstElement.getProject();
final FileEditorManager editorManager =
FileEditorManager.getInstance(project);
final EditorColorsManager editorColorsManager =
EditorColorsManager.getInstance();
final FileEditorManager editorManager = FileEditorManager.getInstance(project);
final EditorColorsManager editorColorsManager = EditorColorsManager.getInstance();
final Editor editor = editorManager.getSelectedTextEditor();
if (editor == null) {
return;
}
final EditorColorsScheme globalScheme =
editorColorsManager.getGlobalScheme();
final TextAttributes textattributes =
globalScheme.getAttributes(
final EditorColorsScheme globalScheme = editorColorsManager.getGlobalScheme();
final TextAttributes textattributes = globalScheme.getAttributes(
EditorColors.SEARCH_RESULT_ATTRIBUTES);
final HighlightManager highlightManager =
HighlightManager.getInstance(project);
highlightManager.addOccurrenceHighlights(
editor, elements, textattributes, true, null);
final FindManager findmanager =
FindManager.getInstance(project);
final HighlightManager highlightManager = HighlightManager.getInstance(project);
highlightManager.addOccurrenceHighlights(editor, elements, textattributes, true, null);
final FindManager findmanager = FindManager.getInstance(project);
FindModel findmodel = findmanager.getFindNextModel();
if (findmodel == null) {
findmodel = findmanager.getFindInFileModel();
@@ -86,11 +79,11 @@ public class HighlightUtil {
findmanager.setFindNextModel(findmodel);
application.invokeLater(new Runnable() {
public void run() {
final WindowManager windowManager =
WindowManager.getInstance();
final StatusBar statusBar =
windowManager.getStatusBar(project);
statusBar.setInfo(statusBarText);
final WindowManager windowManager = WindowManager.getInstance();
final StatusBar statusBar = windowManager.getStatusBar(project);
if (statusBar != null) {
statusBar.setInfo(statusBarText);
}
}
});
}
@@ -102,4 +95,27 @@ public class HighlightUtil {
final List<PsiElement> elements = Collections.singletonList(element);
highlightElements(elements, statusBarText);
}
public static String getPresentableText(PsiElement element) {
return getPresentableText(element, new StringBuilder()).toString();
}
private static StringBuilder getPresentableText(PsiElement element, StringBuilder builder) {
if (element == null) {
return builder;
}
if (element instanceof PsiWhiteSpace) {
return builder.append(' ');
}
final PsiElement[] children = element.getChildren();
if (children.length != 0) {
for (PsiElement child : children) {
getPresentableText(child, builder);
}
}
else {
builder.append(element.getText());
}
return builder;
}
}