IDEA-147586 (Replace StringBuilder with String does not keep final modifier)

This commit is contained in:
Bas Leijdekkers
2015-11-11 19:45:45 +01:00
parent 46fd9b2a9d
commit a0ef7d2adf
2 changed files with 364 additions and 332 deletions
@@ -0,0 +1,355 @@
/*
* Copyright 2003-2015 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.style;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.TypeUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class StringBufferReplaceableByStringInspectionBase extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.display.name");
}
@Override
@NotNull
public String buildErrorString(Object... infos) {
final PsiElement element = (PsiElement)infos[0];
if (element instanceof PsiNewExpression) {
return InspectionGadgetsBundle.message("new.string.buffer.replaceable.by.string.problem.descriptor");
}
final String typeText = ((PsiType)infos[1]).getPresentableText();
return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.problem.descriptor", typeText);
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new StringBufferReplaceableByStringVisitor();
}
private static class StringBufferReplaceableByStringVisitor extends BaseInspectionVisitor {
@Override
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
super.visitLocalVariable(variable);
final PsiType type = variable.getType();
if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type) &&
!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) {
return;
}
final PsiExpression initializer = variable.getInitializer();
if (!isNewStringBufferOrStringBuilder(initializer)) {
return;
}
final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
if (codeBlock == null) {
return;
}
final ReplaceableByStringVisitor visitor = new ReplaceableByStringVisitor(variable);
codeBlock.accept(visitor);
if (!visitor.isReplaceable()) {
return;
}
registerVariableError(variable, variable, type);
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
super.visitNewExpression(expression);
final PsiType type = expression.getType();
if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type) &&
!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) {
return;
}
final PsiExpression completeExpression = getCompleteExpression(expression);
if (completeExpression == null) {
return;
}
registerNewExpressionError(expression, expression, type);
}
private static boolean isNewStringBufferOrStringBuilder(PsiExpression expression) {
if (expression == null) {
return false;
}
if (expression instanceof PsiNewExpression) {
return true;
}
if (!isAppendCall(expression) || !(expression instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
return isNewStringBufferOrStringBuilder(qualifier);
}
}
static boolean isAppendCall(PsiElement element) {
if (!(element instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
@NonNls final String methodName = methodExpression.getReferenceName();
if (!"append".equals(methodName)) {
return false;
}
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length == 3) {
return arguments[0].getType() instanceof PsiArrayType &&
PsiType.INT.equals(arguments[1].getType()) && PsiType.INT.equals(arguments[2].getType());
}
return arguments.length == 1;
}
private static boolean isToStringCall(PsiElement element) {
if (!(element instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
@NonNls final String methodName = methodExpression.getReferenceName();
if (!"toString".equals(methodName)) {
return false;
}
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
return arguments.length == 0;
}
@Nullable
static PsiExpression getCompleteExpression(PsiElement element) {
PsiElement completeExpression = element;
boolean found = false;
while (true) {
final PsiElement parent = completeExpression.getParent();
if (!(parent instanceof PsiReferenceExpression)) {
break;
}
final PsiElement grandParent = parent.getParent();
if (isToStringCall(grandParent)) {
found = true;
} else if (!isAppendCall(grandParent)) {
return null;
}
completeExpression = grandParent;
if (found) {
return (PsiExpression)completeExpression;
}
}
return null;
}
private static class ReplaceableByStringVisitor extends JavaRecursiveElementWalkingVisitor {
private final PsiElement myParent;
private final PsiVariable myVariable;
private boolean myReplaceable = true;
private boolean myPossibleSideEffect;
private boolean myToStringFound;
ReplaceableByStringVisitor(@NotNull PsiVariable variable) {
myVariable = variable;
myParent = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class, PsiIfStatement.class, PsiLoopStatement.class);
}
public boolean isReplaceable() {
return myReplaceable && myToStringFound;
}
@Override
public void visitElement(PsiElement element) {
if (!myReplaceable) {
return;
}
super.visitElement(element);
}
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
super.visitAssignmentExpression(expression);
if (expression.getTextOffset() > myVariable.getTextOffset() && !myToStringFound) {
myPossibleSideEffect = true;
}
}
@Override
public void visitPostfixExpression(PsiPostfixExpression expression) {
super.visitPostfixExpression(expression);
if (expression.getTextOffset() > myVariable.getTextOffset() && !myToStringFound) {
myPossibleSideEffect = true;
}
}
@Override
public void visitPrefixExpression(PsiPrefixExpression expression) {
super.visitPrefixExpression(expression);
if (expression.getTextOffset() > myVariable.getTextOffset() && !myToStringFound) {
myPossibleSideEffect = true;
}
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (expression.getTextOffset() < myVariable.getTextOffset() || myToStringFound) {
return;
}
final PsiMethod method = expression.resolveMethod();
if (method == null) {
myPossibleSideEffect = true;
return;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
myPossibleSideEffect = true;
return;
}
final String name = aClass.getQualifiedName();
if (CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name) ||
CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name)) {
return;
}
if (isArgumentOfStringBuilderMethod(expression)) {
return;
}
myPossibleSideEffect = true;
}
private boolean isArgumentOfStringBuilderMethod(PsiMethodCallExpression expression) {
final PsiExpressionList parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, true, PsiStatement.class);
if (parent == null) {
return false;
}
final PsiElement grandParent = parent.getParent();
if (grandParent instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
if (!isCallToStringBuilderMethod(methodCallExpression)) {
return isArgumentOfStringBuilderMethod(methodCallExpression);
}
return true;
}
if (grandParent instanceof PsiNewExpression) {
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(grandParent, PsiLocalVariable.class, true, PsiExpressionList.class);
if (!myVariable.equals(variable)) {
return false;
}
final PsiNewExpression newExpression = (PsiNewExpression)grandParent;
final PsiMethod constructor = newExpression.resolveMethod();
if (constructor == null) {
return false;
}
final PsiClass aClass = constructor.getContainingClass();
if (aClass == null) {
return false;
}
final String name = aClass.getQualifiedName();
return CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name) ||
CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name);
}
return false;
}
private boolean isCallToStringBuilderMethod(PsiMethodCallExpression methodCallExpression) {
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
PsiExpression qualifier = methodExpression.getQualifierExpression();
while (qualifier instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)qualifier;
final PsiReferenceExpression methodExpression1 = callExpression.getMethodExpression();
qualifier = methodExpression1.getQualifierExpression();
}
if (!(qualifier instanceof PsiReferenceExpression)) {
return false;
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
final PsiElement target = referenceExpression.resolve();
if (!myVariable.equals(target)) {
return false;
}
final PsiMethod method = methodCallExpression.resolveMethod();
if (method == null) {
return false;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
return false;
}
final String name1 = aClass.getQualifiedName();
return CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name1) ||
CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name1);
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
if (!myReplaceable || expression.getTextOffset() < myVariable.getTextOffset()) {
return;
}
super.visitReferenceExpression(expression);
final PsiExpression qualifier = expression.getQualifierExpression();
if (qualifier != null) {
return;
}
final PsiElement target = expression.resolve();
if (!myVariable.equals(target)) {
return;
}
if (myToStringFound) {
myReplaceable = false;
return;
}
final PsiElement element = PsiTreeUtil.getParentOfType(expression, PsiCodeBlock.class, PsiIfStatement.class, PsiLoopStatement.class);
if (!myParent.equals(element)) {
myReplaceable = false;
return;
}
PsiElement parent = expression.getParent();
while (true) {
if (!(parent instanceof PsiReferenceExpression)) {
myReplaceable = false;
return;
}
final PsiElement grandParent = parent.getParent();
if (!isAppendCall(grandParent)) {
if (!isToStringCall(grandParent)) {
myReplaceable = false;
return;
}
myToStringFound = true;
return;
}
if (myPossibleSideEffect) {
myReplaceable = false;
return;
}
parent = grandParent.getParent();
if (parent instanceof PsiExpressionStatement) {
return;
}
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2015 Dave Griffith, Bas Leijdekkers
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,39 +19,23 @@ import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.PsiReplacementUtil;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class StringBufferReplaceableByStringInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.display.name");
}
@Override
@NotNull
public String buildErrorString(Object... infos) {
final PsiElement element = (PsiElement)infos[0];
if (element instanceof PsiNewExpression) {
return InspectionGadgetsBundle.message("new.string.buffer.replaceable.by.string.problem.descriptor");
}
final String typeText = ((PsiType)infos[1]).getPresentableText();
return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.problem.descriptor", typeText);
}
/**
* @author Bas Leijdekkers
*/
public class StringBufferReplaceableByStringInspection extends StringBufferReplaceableByStringInspectionBase {
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
@@ -146,9 +130,11 @@ public class StringBufferReplaceableByStringInspection extends BaseInspection {
if (statement == null) {
return;
}
final String modifier = CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS ? "final " : "";
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiStatement newStatement =
factory.createStatementFromText(CommonClassNames.JAVA_LANG_STRING + ' ' + variable.getName() + '=' + expression + ';', variable);
factory.createStatementFromText(modifier + CommonClassNames.JAVA_LANG_STRING + ' ' + variable.getName() + '=' +
expression + ';', variable);
codeBlock.addBefore(newStatement, statement);
}
variable.delete();
@@ -367,313 +353,4 @@ public class StringBufferReplaceableByStringInspection extends BaseInspection {
}
}
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new StringBufferReplaceableByStringVisitor();
}
private static class StringBufferReplaceableByStringVisitor extends BaseInspectionVisitor {
@Override
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
super.visitLocalVariable(variable);
final PsiType type = variable.getType();
if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type) &&
!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) {
return;
}
final PsiExpression initializer = variable.getInitializer();
if (!isNewStringBufferOrStringBuilder(initializer)) {
return;
}
final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
if (codeBlock == null) {
return;
}
final ReplaceableByStringVisitor visitor = new ReplaceableByStringVisitor(variable);
codeBlock.accept(visitor);
if (!visitor.isReplaceable()) {
return;
}
registerVariableError(variable, variable, type);
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
super.visitNewExpression(expression);
final PsiType type = expression.getType();
if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type) &&
!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) {
return;
}
final PsiExpression completeExpression = getCompleteExpression(expression);
if (completeExpression == null) {
return;
}
registerNewExpressionError(expression, expression, type);
}
private static boolean isNewStringBufferOrStringBuilder(PsiExpression expression) {
if (expression == null) {
return false;
}
if (expression instanceof PsiNewExpression) {
return true;
}
if (!isAppendCall(expression) || !(expression instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
return isNewStringBufferOrStringBuilder(qualifier);
}
}
private static boolean isAppendCall(PsiElement element) {
if (!(element instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
@NonNls final String methodName = methodExpression.getReferenceName();
if (!"append".equals(methodName)) {
return false;
}
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length == 3) {
return arguments[0].getType() instanceof PsiArrayType &&
PsiType.INT.equals(arguments[1].getType()) && PsiType.INT.equals(arguments[2].getType());
}
return arguments.length == 1;
}
private static boolean isToStringCall(PsiElement element) {
if (!(element instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
@NonNls final String methodName = methodExpression.getReferenceName();
if (!"toString".equals(methodName)) {
return false;
}
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
return arguments.length == 0;
}
@Nullable
private static PsiExpression getCompleteExpression(PsiElement element) {
PsiElement completeExpression = element;
boolean found = false;
while (true) {
final PsiElement parent = completeExpression.getParent();
if (!(parent instanceof PsiReferenceExpression)) {
break;
}
final PsiElement grandParent = parent.getParent();
if (isToStringCall(grandParent)) {
found = true;
} else if (!isAppendCall(grandParent)) {
return null;
}
completeExpression = grandParent;
if (found) {
return (PsiExpression)completeExpression;
}
}
return null;
}
private static class ReplaceableByStringVisitor extends JavaRecursiveElementWalkingVisitor {
private final PsiElement myParent;
private final PsiVariable myVariable;
private boolean myReplaceable = true;
private boolean myPossibleSideEffect;
private boolean myToStringFound;
ReplaceableByStringVisitor(@NotNull PsiVariable variable) {
myVariable = variable;
myParent = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class, PsiIfStatement.class, PsiLoopStatement.class);
}
public boolean isReplaceable() {
return myReplaceable && myToStringFound;
}
@Override
public void visitElement(PsiElement element) {
if (!myReplaceable) {
return;
}
super.visitElement(element);
}
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
super.visitAssignmentExpression(expression);
if (expression.getTextOffset() > myVariable.getTextOffset() && !myToStringFound) {
myPossibleSideEffect = true;
}
}
@Override
public void visitPostfixExpression(PsiPostfixExpression expression) {
super.visitPostfixExpression(expression);
if (expression.getTextOffset() > myVariable.getTextOffset() && !myToStringFound) {
myPossibleSideEffect = true;
}
}
@Override
public void visitPrefixExpression(PsiPrefixExpression expression) {
super.visitPrefixExpression(expression);
if (expression.getTextOffset() > myVariable.getTextOffset() && !myToStringFound) {
myPossibleSideEffect = true;
}
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (expression.getTextOffset() < myVariable.getTextOffset() || myToStringFound) {
return;
}
final PsiMethod method = expression.resolveMethod();
if (method == null) {
myPossibleSideEffect = true;
return;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
myPossibleSideEffect = true;
return;
}
final String name = aClass.getQualifiedName();
if (CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name) ||
CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name)) {
return;
}
if (isArgumentOfStringBuilderMethod(expression)) {
return;
}
myPossibleSideEffect = true;
}
private boolean isArgumentOfStringBuilderMethod(PsiMethodCallExpression expression) {
final PsiExpressionList parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, true, PsiStatement.class);
if (parent == null) {
return false;
}
final PsiElement grandParent = parent.getParent();
if (grandParent instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
if (!isCallToStringBuilderMethod(methodCallExpression)) {
return isArgumentOfStringBuilderMethod(methodCallExpression);
}
return true;
}
if (grandParent instanceof PsiNewExpression) {
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(grandParent, PsiLocalVariable.class, true, PsiExpressionList.class);
if (!myVariable.equals(variable)) {
return false;
}
final PsiNewExpression newExpression = (PsiNewExpression)grandParent;
final PsiMethod constructor = newExpression.resolveMethod();
if (constructor == null) {
return false;
}
final PsiClass aClass = constructor.getContainingClass();
if (aClass == null) {
return false;
}
final String name = aClass.getQualifiedName();
return CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name) ||
CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name);
}
return false;
}
private boolean isCallToStringBuilderMethod(PsiMethodCallExpression methodCallExpression) {
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
PsiExpression qualifier = methodExpression.getQualifierExpression();
while (qualifier instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)qualifier;
final PsiReferenceExpression methodExpression1 = callExpression.getMethodExpression();
qualifier = methodExpression1.getQualifierExpression();
}
if (!(qualifier instanceof PsiReferenceExpression)) {
return false;
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
final PsiElement target = referenceExpression.resolve();
if (!myVariable.equals(target)) {
return false;
}
final PsiMethod method = methodCallExpression.resolveMethod();
if (method == null) {
return false;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
return false;
}
final String name1 = aClass.getQualifiedName();
return CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name1) ||
CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name1);
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
if (!myReplaceable || expression.getTextOffset() < myVariable.getTextOffset()) {
return;
}
super.visitReferenceExpression(expression);
final PsiExpression qualifier = expression.getQualifierExpression();
if (qualifier != null) {
return;
}
final PsiElement target = expression.resolve();
if (!myVariable.equals(target)) {
return;
}
if (myToStringFound) {
myReplaceable = false;
return;
}
final PsiElement element = PsiTreeUtil.getParentOfType(expression, PsiCodeBlock.class, PsiIfStatement.class, PsiLoopStatement.class);
if (!myParent.equals(element)) {
myReplaceable = false;
return;
}
PsiElement parent = expression.getParent();
while (true) {
if (!(parent instanceof PsiReferenceExpression)) {
myReplaceable = false;
return;
}
final PsiElement grandParent = parent.getParent();
if (!isAppendCall(grandParent)) {
if (!isToStringCall(grandParent)) {
myReplaceable = false;
return;
}
myToStringFound = true;
return;
}
if (myPossibleSideEffect) {
myReplaceable = false;
return;
}
parent = grandParent.getParent();
if (parent instanceof PsiExpressionStatement) {
return;
}
}
}
}
}