blob: 941fc702cae4e72c36b451c9542d9dcadaf12d58 [file] [log] [blame]
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.ui.DocumentAdapter;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;
/**
* @author max
*/
public class ComparingReferencesInspection extends BaseJavaLocalInspectionTool {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection");
private final LocalQuickFix myQuickFix = new MyQuickFix();
@SuppressWarnings({"WeakerAccess"}) @NonNls public String CHECKED_CLASSES = "java.lang.String;java.util.Date";
@NonNls private static final String DESCRIPTION_TEMPLATE = InspectionsBundle.message("inspection.comparing.references.problem.descriptor");
@NotNull
public String getDisplayName() {
return "'==' or '!=' instead of 'equals()'";
}
@NotNull
public String getGroupDisplayName() {
return GroupNames.BUGS_GROUP_NAME;
}
@NotNull
public String getShortName() {
return "ComparingReferences";
}
private boolean isCheckedType(PsiType type) {
if (!(type instanceof PsiClassType)) return false;
StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";");
while (tokenizer.hasMoreTokens()) {
String className = tokenizer.nextToken();
if (type.equalsToText(className)) return true;
}
return false;
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) {
}
@Override public void visitBinaryExpression(PsiBinaryExpression expression) {
super.visitBinaryExpression(expression);
IElementType opSign = expression.getOperationTokenType();
if (opSign == JavaTokenType.EQEQ || opSign == JavaTokenType.NE) {
PsiExpression lOperand = expression.getLOperand();
PsiExpression rOperand = expression.getROperand();
if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) return;
PsiType lType = lOperand.getType();
PsiType rType = rOperand.getType();
if (isCheckedType(lType) || isCheckedType(rType)) {
holder.registerProblem(expression,
DESCRIPTION_TEMPLATE, myQuickFix);
}
}
}
};
}
private static boolean isNullLiteral(PsiExpression expr) {
return expr instanceof PsiLiteralExpression && "null".equals(expr.getText());
}
private static class MyQuickFix implements LocalQuickFix {
@NotNull
public String getName() {
// The test (see the TestThisPlugin class) uses this string to identify the quick fix action.
return InspectionsBundle.message("inspection.comparing.references.use.quickfix");
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
PsiBinaryExpression binaryExpression = (PsiBinaryExpression)descriptor.getPsiElement();
IElementType opSign = binaryExpression.getOperationTokenType();
PsiExpression lExpr = binaryExpression.getLOperand();
PsiExpression rExpr = binaryExpression.getROperand();
if (rExpr == null)
return;
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiMethodCallExpression equalsCall = (PsiMethodCallExpression)factory.createExpressionFromText("a.equals(b)", null);
equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr);
equalsCall.getArgumentList().getExpressions()[0].replace(rExpr);
PsiExpression result = (PsiExpression)binaryExpression.replace(equalsCall);
if (opSign == JavaTokenType.NE) {
PsiPrefixExpression negation = (PsiPrefixExpression)factory.createExpressionFromText("!a", null);
negation.getOperand().replace(result);
result.replace(negation);
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
public JComponent createOptionsPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
final JTextField checkedClasses = new JTextField(CHECKED_CLASSES);
checkedClasses.getDocument().addDocumentListener(new DocumentAdapter() {
public void textChanged(DocumentEvent event) {
CHECKED_CLASSES = checkedClasses.getText();
}
});
panel.add(checkedClasses);
return panel;
}
public boolean isEnabledByDefault() {
return true;
}
}