2 * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.siyeh.ig.equality;
18 import com.intellij.codeInspection.ProblemDescriptor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.psi.*;
21 import com.intellij.psi.tree.IElementType;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.psi.util.PsiUtil;
24 import com.intellij.psi.util.TypeConversionUtil;
25 import com.siyeh.InspectionGadgetsBundle;
26 import com.siyeh.ig.BaseInspection;
27 import com.siyeh.ig.BaseInspectionVisitor;
28 import com.siyeh.ig.InspectionGadgetsFix;
29 import com.siyeh.ig.PsiReplacementUtil;
30 import com.siyeh.ig.psiutils.ClassUtils;
31 import com.siyeh.ig.psiutils.ParenthesesUtils;
32 import org.jetbrains.annotations.Nls;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
36 public class EqualityOperatorComparesObjectsInspection extends BaseInspection {
41 public String getDisplayName() {
42 return InspectionGadgetsBundle.message("equality.operator.compares.objects.name");
47 protected String buildErrorString(Object... infos) {
48 return InspectionGadgetsBundle.message("equality.operator.compares.objects.descriptor", infos);
52 public BaseInspectionVisitor buildVisitor() {
53 return new ObjectEqualityVisitor();
58 protected InspectionGadgetsFix[] buildFixes(Object... infos) {
59 return new InspectionGadgetsFix[]{new EqualsFix(infos), new SafeEqualsFix(infos)};
62 private static void doFixImpl(@NotNull PsiElement element) {
63 final PsiBinaryExpression exp = (PsiBinaryExpression)element;
64 final PsiExpression lhs = exp.getLOperand();
65 final PsiExpression rhs = exp.getROperand();
69 final PsiExpression strippedLhs = ParenthesesUtils.stripParentheses(lhs);
70 if (strippedLhs == null) {
73 final PsiExpression strippedRhs = ParenthesesUtils.stripParentheses(rhs);
74 if (strippedRhs == null) {
77 final String lhText = strippedLhs.getText();
78 final String rhText = strippedRhs.getText();
80 final String prefix = exp.getOperationTokenType().equals(JavaTokenType.EQEQ) ? "" : "!";
81 @NonNls final String expString;
82 if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) {
83 expString = prefix + '(' + lhText + ").equals(" + rhText + ')';
86 expString = prefix + lhText + ".equals(" + rhText + ')';
88 PsiReplacementUtil.replaceExpression(exp, expString);
91 private static void doSafeFixImpl(PsiElement element) {
92 final PsiBinaryExpression exp = (PsiBinaryExpression)element;
93 final PsiExpression lhs = exp.getLOperand();
94 final PsiExpression rhs = exp.getROperand();
98 final PsiExpression strippedLhs =
99 ParenthesesUtils.stripParentheses(lhs);
100 if (strippedLhs == null) {
103 final PsiExpression strippedRhs =
104 ParenthesesUtils.stripParentheses(rhs);
105 if (strippedRhs == null) {
108 final String lhsText = strippedLhs.getText();
109 final String rhsText = strippedRhs.getText();
110 final PsiJavaToken operationSign = exp.getOperationSign();
111 final IElementType tokenType = operationSign.getTokenType();
112 final String signText = operationSign.getText();
113 @NonNls final StringBuilder newExpression = new StringBuilder();
114 if (PsiUtil.isLanguageLevel7OrHigher(element) && ClassUtils.findClass("java.util.Objects", element) != null) {
115 if (tokenType.equals(JavaTokenType.NE)) {
116 newExpression.append('!');
118 newExpression.append("java.util.Objects.equals(").append(lhsText).append(',').append(rhsText).append(')');
121 newExpression.append(lhsText).append("==null?").append(rhsText).append(signText).append(" null:");
122 if (tokenType.equals(JavaTokenType.NE)) {
123 newExpression.append('!');
125 if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) {
126 newExpression.append('(').append(lhsText).append(')');
129 newExpression.append(lhsText);
131 newExpression.append(".equals(").append(rhsText).append(')');
133 PsiReplacementUtil.replaceExpressionAndShorten(exp, newExpression.toString());
136 private static class SafeEqualsFix extends InspectionGadgetsFix {
137 private final Object[] myInfos;
139 public SafeEqualsFix(Object... infos) {
146 public String getName() {
147 return InspectionGadgetsBundle.message("equality.operator.compares.objects.safe.quickfix", myInfos);
153 public String getFamilyName() {
154 return InspectionGadgetsBundle.message("equality.operator.compares.objects.safe.family.quickfix");
158 protected void doFix(Project project, ProblemDescriptor descriptor) {
159 doSafeFixImpl(descriptor.getPsiElement());
163 private static class EqualsFix extends InspectionGadgetsFix {
164 private final Object[] myInfos;
166 public EqualsFix(Object... infos) {
173 public String getName() {
174 return InspectionGadgetsBundle.message("equality.operator.compares.objects.quickfix", myInfos);
180 public String getFamilyName() {
181 return InspectionGadgetsBundle.message("equality.operator.compares.objects.family.quickfix");
185 protected void doFix(Project project, ProblemDescriptor descriptor) {
186 doFixImpl(descriptor.getPsiElement());
190 private static class ObjectEqualityVisitor extends BaseInspectionVisitor {
192 public void visitBinaryExpression(PsiBinaryExpression expression) {
193 super.visitBinaryExpression(expression);
194 final IElementType tokenType = expression.getOperationTokenType();
195 if (!tokenType.equals(JavaTokenType.NE) &&
196 !tokenType.equals(JavaTokenType.EQEQ)) {
199 final PsiExpression lhs = expression.getLOperand();
200 final PsiType lhsType = lhs.getType();
201 if (lhsType == null || lhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(lhsType)) {
204 final PsiExpression rhs = expression.getROperand();
208 final PsiType rhsType = rhs.getType();
209 if (rhsType == null || rhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(rhsType)) {
212 if (lhs instanceof PsiThisExpression || rhs instanceof PsiThisExpression) {
213 final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
214 if (method != null && "equals".equals(method.getName())) {
218 final String operationText = expression.getOperationSign().getText();
219 final String prefix = tokenType.equals(JavaTokenType.NE) ? "!" : "";
220 registerError(expression, operationText, prefix);