added option to ignore casts from int to char
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / numeric / CastThatLosesPrecisionInspection.java
blob5d183bc9c1c0e178fc38545d18f98bb973f14e4d
1 /*
2 * Copyright 2003-2008 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.numeric;
18 import com.intellij.psi.*;
19 import com.intellij.openapi.project.Project;
20 import com.siyeh.InspectionGadgetsBundle;
21 import com.siyeh.ig.BaseInspection;
22 import com.siyeh.ig.BaseInspectionVisitor;
23 import com.siyeh.ig.ui.SingleCheckboxOptionsPanel;
24 import com.siyeh.ig.psiutils.ClassUtils;
25 import org.jetbrains.annotations.NotNull;
27 import javax.swing.JComponent;
28 import java.util.HashMap;
29 import java.util.Map;
31 public class CastThatLosesPrecisionInspection extends BaseInspection {
33 @SuppressWarnings({"PublicField"})
34 public boolean ignoreIntegerCharCasts = false;
36 /** @noinspection StaticCollection */
37 private static final Map<PsiType, Integer> s_typePrecisions =
38 new HashMap<PsiType, Integer>(7);
40 static {
41 s_typePrecisions.put(PsiType.BYTE, 1);
42 s_typePrecisions.put(PsiType.CHAR, 2);
43 s_typePrecisions.put(PsiType.SHORT, 2);
44 s_typePrecisions.put(PsiType.INT, 3);
45 s_typePrecisions.put(PsiType.LONG, 4);
46 s_typePrecisions.put(PsiType.FLOAT, 5);
47 s_typePrecisions.put(PsiType.DOUBLE, 6);
50 @Override
51 @NotNull
52 public String getID() {
53 return "NumericCastThatLosesPrecision";
56 @Override
57 @NotNull
58 public String getDisplayName() {
59 return InspectionGadgetsBundle.message(
60 "cast.that.loses.precision.display.name");
63 @Override
64 @NotNull
65 public String buildErrorString(Object... infos) {
66 final PsiType operandType = (PsiType)infos[0];
67 return InspectionGadgetsBundle.message(
68 "cast.that.loses.precision.problem.descriptor",
69 operandType.getPresentableText());
72 @Override
73 public JComponent createOptionsPanel() {
74 return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message(
75 "cast.that.loses.precision.option"),
76 this, "ignoreIntegerCharCasts");
79 @Override
80 public BaseInspectionVisitor buildVisitor() {
81 return new CastThatLosesPrecisionVisitor();
84 private class CastThatLosesPrecisionVisitor
85 extends BaseInspectionVisitor {
87 @Override public void visitTypeCastExpression(
88 @NotNull PsiTypeCastExpression expression) {
89 final PsiType castType = expression.getType();
90 if (!ClassUtils.isPrimitiveNumericType(castType)) {
91 return;
93 final PsiExpression operand = expression.getOperand();
94 if (operand == null) {
95 return;
97 final PsiType operandType = operand.getType();
98 if (!ClassUtils.isPrimitiveNumericType(operandType)) {
99 return;
101 if (hasLowerPrecision(operandType, castType)) {
102 return;
104 if (ignoreIntegerCharCasts) {
105 if (operandType == PsiType.INT &&
106 castType == PsiType.CHAR) {
107 return;
110 final Project project = expression.getProject();
111 final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
112 final PsiConstantEvaluationHelper evaluationHelper =
113 psiFacade.getConstantEvaluationHelper();
114 Object result =
115 evaluationHelper.computeConstantExpression(operand);
117 if (result instanceof Character) {
118 result = Integer.valueOf(((Character) result).charValue());
120 if (result instanceof Number) {
121 final Number number = (Number)result;
122 if (valueIsContainableInType(number, castType)) {
123 return;
126 final PsiTypeElement castTypeElement = expression.getCastType();
127 if (castTypeElement == null) {
128 return;
130 registerError(castTypeElement, operandType);
133 private boolean hasLowerPrecision(PsiType operandType,
134 PsiType castType) {
135 final Integer operandPrecision = s_typePrecisions.get(operandType);
136 final Integer castPrecision = s_typePrecisions.get(castType);
137 return operandPrecision <= castPrecision;
140 private boolean valueIsContainableInType(Number value,
141 PsiType type) {
142 final long longValue = value.longValue();
143 final double doubleValue = value.doubleValue();
144 if (PsiType.BYTE.equals(type)) {
145 return longValue >= (long)Byte.MIN_VALUE &&
146 longValue <= (long)Byte.MAX_VALUE &&
147 doubleValue >= (double)Byte.MIN_VALUE &&
148 doubleValue <= (double)Byte.MAX_VALUE;
149 } else if (PsiType.CHAR.equals(type)) {
150 return longValue >= (long)Character.MIN_VALUE &&
151 longValue <= (long)Character.MAX_VALUE &&
152 doubleValue >= (double)Character.MIN_VALUE &&
153 doubleValue <= (double)Character.MAX_VALUE;
154 } else if (PsiType.SHORT.equals(type)) {
155 return longValue >= (long)Short.MIN_VALUE &&
156 longValue <= (long)Short.MAX_VALUE &&
157 doubleValue >= (double)Short.MIN_VALUE &&
158 doubleValue <= (double)Short.MAX_VALUE;
159 } else if (PsiType.INT.equals(type)) {
160 return longValue >= (long)Integer.MIN_VALUE &&
161 longValue <= (long)Integer.MAX_VALUE &&
162 doubleValue >= (double)Integer.MIN_VALUE &&
163 doubleValue <= (double)Integer.MAX_VALUE;
164 } else if (PsiType.LONG.equals(type)) {
165 return longValue >= Long.MIN_VALUE &&
166 longValue <= Long.MAX_VALUE &&
167 doubleValue >= (double)Long.MIN_VALUE &&
168 doubleValue <= (double)Long.MAX_VALUE;
169 } else if (PsiType.FLOAT.equals(type)) {
170 return doubleValue == value.floatValue();
171 } else if (PsiType.DOUBLE.equals(type)) {
172 return true;
174 return false;