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.PsiUtil;
23 import com.intellij.psi.util.TypeConversionUtil;
24 import com.siyeh.InspectionGadgetsBundle;
25 import com.siyeh.ig.BaseInspection;
26 import com.siyeh.ig.BaseInspectionVisitor;
27 import com.siyeh.ig.InspectionGadgetsFix;
28 import com.siyeh.ig.PsiReplacementUtil;
29 import com.siyeh.ig.psiutils.ClassUtils;
30 import com.siyeh.ig.psiutils.ParenthesesUtils;
31 import org.jetbrains.annotations.Nls;
32 import org.jetbrains.annotations.NonNls;
33 import org.jetbrains.annotations.NotNull;
35 public class EqualityOperatorComparesObjectsInspection extends BaseInspection {
40 public String getDisplayName() {
41 return InspectionGadgetsBundle.message("equality.operator.compares.objects.name");
46 protected String buildErrorString(Object... infos) {
47 return InspectionGadgetsBundle.message("equality.operator.compares.objects.descriptor", infos);
51 public BaseInspectionVisitor buildVisitor() {
52 return new ObjectEqualityVisitor();
57 protected InspectionGadgetsFix[] buildFixes(Object... infos) {
58 return new InspectionGadgetsFix[]{new EqualsFix(infos), new SafeEqualsFix(infos)};
61 private static void doFixImpl(@NotNull PsiElement element) {
62 final PsiBinaryExpression exp = (PsiBinaryExpression)element;
63 final PsiExpression lhs = exp.getLOperand();
64 final PsiExpression rhs = exp.getROperand();
68 final PsiExpression strippedLhs = ParenthesesUtils.stripParentheses(lhs);
69 if (strippedLhs == null) {
72 final PsiExpression strippedRhs = ParenthesesUtils.stripParentheses(rhs);
73 if (strippedRhs == null) {
76 final String lhText = strippedLhs.getText();
77 final String rhText = strippedRhs.getText();
79 final String prefix = exp.getOperationTokenType().equals(JavaTokenType.EQEQ) ? "" : "!";
80 @NonNls final String expString;
81 if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) {
82 expString = prefix + '(' + lhText + ").equals(" + rhText + ')';
85 expString = prefix + lhText + ".equals(" + rhText + ')';
87 PsiReplacementUtil.replaceExpression(exp, expString);
90 private static void doSafeFixImpl(PsiElement element) {
91 final PsiBinaryExpression exp = (PsiBinaryExpression)element;
92 final PsiExpression lhs = exp.getLOperand();
93 final PsiExpression rhs = exp.getROperand();
97 final PsiExpression strippedLhs =
98 ParenthesesUtils.stripParentheses(lhs);
99 if (strippedLhs == null) {
102 final PsiExpression strippedRhs =
103 ParenthesesUtils.stripParentheses(rhs);
104 if (strippedRhs == null) {
107 final String lhsText = strippedLhs.getText();
108 final String rhsText = strippedRhs.getText();
109 final PsiJavaToken operationSign = exp.getOperationSign();
110 final IElementType tokenType = operationSign.getTokenType();
111 final String signText = operationSign.getText();
112 @NonNls final StringBuilder newExpression = new StringBuilder();
113 if (PsiUtil.isLanguageLevel7OrHigher(element) && ClassUtils.findClass("java.util.Objects", element) != null) {
114 if (tokenType.equals(JavaTokenType.NE)) {
115 newExpression.append('!');
117 newExpression.append("java.util.Objects.equals(").append(lhsText).append(',').append(rhsText).append(')');
120 newExpression.append(lhsText).append("==null?").append(rhsText).append(signText).append(" null:");
121 if (tokenType.equals(JavaTokenType.NE)) {
122 newExpression.append('!');
124 if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) {
125 newExpression.append('(').append(lhsText).append(')');
128 newExpression.append(lhsText);
130 newExpression.append(".equals(").append(rhsText).append(')');
132 PsiReplacementUtil.replaceExpressionAndShorten(exp, newExpression.toString());
135 private static class SafeEqualsFix extends InspectionGadgetsFix {
136 private final Object[] myInfos;
138 public SafeEqualsFix(Object... infos) {
145 public String getName() {
146 return InspectionGadgetsBundle.message("equality.operator.compares.objects.safe.quickfix", myInfos);
152 public String getFamilyName() {
153 return InspectionGadgetsBundle.message("equality.operator.compares.objects.safe.family.quickfix");
157 protected void doFix(Project project, ProblemDescriptor descriptor) {
158 doSafeFixImpl(descriptor.getPsiElement());
162 private static class EqualsFix extends InspectionGadgetsFix {
163 private final Object[] myInfos;
165 public EqualsFix(Object... infos) {
172 public String getName() {
173 return InspectionGadgetsBundle.message("equality.operator.compares.objects.quickfix", myInfos);
179 public String getFamilyName() {
180 return InspectionGadgetsBundle.message("equality.operator.compares.objects.family.quickfix");
184 protected void doFix(Project project, ProblemDescriptor descriptor) {
185 doFixImpl(descriptor.getPsiElement());
189 private static class ObjectEqualityVisitor extends BaseInspectionVisitor {
191 public void visitBinaryExpression(PsiBinaryExpression expression) {
192 super.visitBinaryExpression(expression);
193 final IElementType tokenType = expression.getOperationTokenType();
194 if (!tokenType.equals(JavaTokenType.NE) &&
195 !tokenType.equals(JavaTokenType.EQEQ)) {
198 final PsiExpression lhs = expression.getLOperand();
199 final PsiType lhsType = lhs.getType();
200 if (lhsType == null || lhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(lhsType)) {
203 final PsiExpression rhs = expression.getROperand();
207 final PsiType rhsType = rhs.getType();
208 if (rhsType == null || rhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(rhsType)) {
211 final String operationText = expression.getOperationSign().getText();
212 final String prefix = tokenType.equals(JavaTokenType.NE) ? "!" : "";
213 registerError(expression, operationText, prefix);