blob: 69c2bad8242b211bb39a3a97949375bed78fc10d [file] [log] [blame]
/*
* Copyright 2003-2014 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.performance;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.HardcodedMethodConstants;
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.BoolUtils;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class StringEqualsEmptyStringInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message("string.equals.empty.string.display.name");
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
final boolean useIsEmpty = ((Boolean)infos[0]).booleanValue();
if (useIsEmpty) {
return InspectionGadgetsBundle.message("string.equals.empty.string.is.empty.problem.descriptor");
} else {
return InspectionGadgetsBundle.message("string.equals.empty.string.problem.descriptor");
}
}
@Override
public InspectionGadgetsFix buildFix(Object... infos) {
final boolean useIsEmpty = ((Boolean)infos[0]).booleanValue();
return new StringEqualsEmptyStringFix(useIsEmpty);
}
private static class StringEqualsEmptyStringFix extends InspectionGadgetsFix {
private final boolean useIsEmpty;
public StringEqualsEmptyStringFix(boolean useIsEmpty) {
this.useIsEmpty = useIsEmpty;
}
@Override
@NotNull
public String getName() {
if (useIsEmpty) {
return InspectionGadgetsBundle.message("string.equals.empty.string.isempty.quickfix");
}
else {
return InspectionGadgetsBundle.message("string.equals.empty.string.quickfix");
}
}
@NotNull
@Override
public String getFamilyName() {
return "Simplify empty string check";
}
@Override
public void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
final PsiIdentifier name = (PsiIdentifier)descriptor.getPsiElement();
final PsiReferenceExpression expression = (PsiReferenceExpression)name.getParent();
if (expression == null) {
return;
}
final PsiMethodCallExpression call = (PsiMethodCallExpression)expression.getParent();
final PsiExpressionList argumentList = call.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length == 0) {
return;
}
final PsiExpression qualifier = expression.getQualifierExpression();
final PsiExpression argument = arguments[0];
final PsiExpression checkedExpression;
final boolean addNullCheck;
if (ExpressionUtils.isEmptyStringLiteral(argument)) {
checkedExpression = getCheckedExpression(qualifier);
addNullCheck = false;
}
else {
checkedExpression = getCheckedExpression(argument);
addNullCheck = !isCheckedForNull(checkedExpression);
}
final StringBuilder newExpression;
if (addNullCheck) {
newExpression = new StringBuilder(checkedExpression.getText());
newExpression.append("!=null&&");
} else {
newExpression = new StringBuilder("");
}
final PsiElement parent = call.getParent();
final PsiExpression expressionToReplace;
if (parent instanceof PsiExpression) {
final PsiExpression parentExpression = (PsiExpression)parent;
if (BoolUtils.isNegation(parentExpression)) {
expressionToReplace = parentExpression;
if (useIsEmpty) {
newExpression.append('!').append(checkedExpression.getText()).append(".isEmpty()");
}
else {
newExpression.append(checkedExpression.getText()).append(".length()!=0");
}
}
else {
expressionToReplace = call;
if (useIsEmpty) {
newExpression.append(checkedExpression.getText()).append(".isEmpty()");
}
else {
newExpression.append(checkedExpression.getText()).append(".length()==0");
}
}
}
else {
expressionToReplace = call;
if (useIsEmpty) {
newExpression.append(checkedExpression.getText()).append(".isEmpty()");
}
else {
newExpression.append(checkedExpression.getText()).append(".length()==0");
}
}
PsiReplacementUtil.replaceExpression(expressionToReplace, newExpression.toString());
}
private static boolean isCheckedForNull(PsiExpression expression) {
final PsiPolyadicExpression polyadicExpression =
PsiTreeUtil.getParentOfType(expression, PsiPolyadicExpression.class, true, PsiStatement.class, PsiVariable.class);
if (polyadicExpression == null) {
return false;
}
final IElementType tokenType = polyadicExpression.getOperationTokenType();
for (PsiExpression operand : polyadicExpression.getOperands()) {
if (PsiTreeUtil.isAncestor(operand, expression, true)) {
return false;
}
if (!(operand instanceof PsiBinaryExpression)) {
continue;
}
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)operand;
final IElementType operationTokenType = binaryExpression.getOperationTokenType();
if (JavaTokenType.ANDAND.equals(tokenType)) {
if (!JavaTokenType.NE.equals(operationTokenType)) {
continue;
}
}
else if (JavaTokenType.OROR.equals(tokenType)) {
if (!JavaTokenType.EQEQ.equals(operationTokenType)) {
continue;
}
}
else {
continue;
}
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
if (rhs == null) {
continue;
}
if (PsiType.NULL.equals(lhs.getType()) && EquivalenceChecker.expressionsAreEquivalent(expression, rhs)) {
return true;
}
else if (PsiType.NULL.equals(rhs.getType()) && EquivalenceChecker.expressionsAreEquivalent(expression, lhs)) {
return true;
}
}
return false;
}
private PsiExpression getCheckedExpression(PsiExpression expression) {
if (useIsEmpty || !(expression instanceof PsiMethodCallExpression)) {
return expression;
}
// to replace stringBuffer.toString().equals("") with
// stringBuffer.length() == 0
final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)expression;
final PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
final String referenceName = methodExpression.getReferenceName();
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
if (qualifierExpression == null) {
return expression;
}
final PsiType type = qualifierExpression.getType();
if (HardcodedMethodConstants.TO_STRING.equals(referenceName) && type != null && (type.equalsToText(
CommonClassNames.JAVA_LANG_STRING_BUFFER) || type.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUILDER))) {
return qualifierExpression;
}
else {
return expression;
}
}
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new StringEqualsEmptyStringVisitor();
}
private static class StringEqualsEmptyStringVisitor extends BaseInspectionVisitor {
@Override
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
super.visitMethodCallExpression(call);
final PsiReferenceExpression methodExpression = call.getMethodExpression();
@NonNls final String methodName = methodExpression.getReferenceName();
if (!"equals".equals(methodName)) {
return;
}
final PsiExpressionList argumentList = call.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length != 1) {
return;
}
final PsiElement context = call.getParent();
final boolean useIsEmpty = PsiUtil.isLanguageLevel6OrHigher(call);
if (!useIsEmpty && context instanceof PsiExpressionStatement) {
// cheesy, but necessary, because otherwise the quickfix will
// produce uncompilable code (out of merely incorrect code).
return;
}
final PsiExpression qualifier = methodExpression.getQualifierExpression();
final PsiExpression argument = arguments[0];
if (ExpressionUtils.isEmptyStringLiteral(qualifier)) {
final PsiType type = argument.getType();
if (!TypeUtils.isJavaLangString(type)) {
return;
}
}
else if (ExpressionUtils.isEmptyStringLiteral(argument)) {
if (qualifier == null) {
return;
}
final PsiType type = qualifier.getType();
if (!TypeUtils.isJavaLangString(type)) {
return;
}
}
else {
return;
}
registerMethodCallError(call, Boolean.valueOf(useIsEmpty));
}
}
}