| 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; |
| } |
| } |