IDEADEV-35167 (Confusing "confusing floating-point literal" and wrong quick-fix)
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / numeric / ConfusingFloatingPointLiteralInspection.java
blob2b0c2469778b26a4bf1680d631bb8282fe7d215c
1 /*
2 * Copyright 2003-2009 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.codeInspection.ProblemDescriptor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.psi.PsiExpression;
21 import com.intellij.psi.PsiLiteralExpression;
22 import com.intellij.psi.PsiType;
23 import com.intellij.util.IncorrectOperationException;
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 org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
31 public class ConfusingFloatingPointLiteralInspection
32 extends BaseInspection {
34 @Override
35 @NotNull
36 public String getDisplayName() {
37 return InspectionGadgetsBundle.message(
38 "confusing.floating.point.literal.display.name");
41 @Override
42 @NotNull
43 protected String buildErrorString(Object... infos) {
44 return InspectionGadgetsBundle.message(
45 "confusing.floating.point.literal.problem.descriptor");
48 @Override
49 public InspectionGadgetsFix buildFix(Object... infos) {
50 return new ConfusingFloatingPointLiteralFix();
53 private static class ConfusingFloatingPointLiteralFix
54 extends InspectionGadgetsFix {
56 @NotNull
57 public String getName() {
58 return InspectionGadgetsBundle.message(
59 "confusing.floating.point.literal.change.quickfix");
62 @Override
63 public void doFix(Project project, ProblemDescriptor descriptor)
64 throws IncorrectOperationException {
65 final PsiExpression literalExpression =
66 (PsiExpression)descriptor.getPsiElement();
67 final String text = literalExpression.getText();
68 final String newText = getCanonicalForm(text);
69 replaceExpression(literalExpression, newText);
72 private static String getCanonicalForm(String text) {
73 final boolean isHexadecimal =
74 text.startsWith("0x") || text.startsWith("0X");
75 int breakPoint = text.indexOf((int)'e');
76 if (breakPoint < 0) {
77 breakPoint = text.indexOf((int)'E');
79 if (breakPoint < 0) {
80 breakPoint = text.indexOf((int)'f');
82 if (breakPoint < 0) {
83 breakPoint = text.indexOf((int)'F');
85 if (breakPoint < 0) {
86 breakPoint = text.indexOf((int)'p');
88 if (breakPoint < 0) {
89 breakPoint = text.indexOf((int)'P');
91 if (breakPoint < 0) {
92 breakPoint = text.indexOf((int)'d');
94 if (breakPoint < 0) {
95 breakPoint = text.indexOf((int)'D');
97 final String suffix;
98 final String prefix;
99 if (breakPoint < 0) {
100 suffix = "";
101 prefix = text;
102 } else {
103 suffix = text.substring(breakPoint);
104 prefix = text.substring(0, breakPoint);
106 final int indexPoint = prefix.indexOf((int)'.');
107 if (indexPoint < 0) {
108 return prefix + ".0" + suffix;
109 } else if (isHexadecimal && indexPoint == 2) {
110 return prefix.substring(0, 2) + '0' + prefix.substring(2) + suffix;
111 } else if (indexPoint == 0) {
112 return '0' + prefix + suffix;
113 } else {
114 return prefix + '0' + suffix;
120 @Override
121 public BaseInspectionVisitor buildVisitor() {
122 return new ConfusingFloatingPointLiteralVisitor();
125 private static class ConfusingFloatingPointLiteralVisitor
126 extends BaseInspectionVisitor {
128 @Override public void visitLiteralExpression(
129 @NotNull PsiLiteralExpression literal) {
130 super.visitLiteralExpression(literal);
131 final PsiType type = literal.getType();
132 if (type == null) {
133 return;
135 if (!(type.equals(PsiType.FLOAT) || type.equals(PsiType.DOUBLE))) {
136 return;
138 final String text = literal.getText();
139 if (text == null) {
140 return;
142 if (!isConfusing(text)) {
143 return;
145 registerError(literal);
149 private static boolean isConfusing(@Nullable CharSequence text) {
150 if (text == null) {
151 return false;
153 final int length = text.length();
154 if (length < 3) {
155 return true;
157 boolean hexadecimal = true;
158 final char firstChar = text.charAt(0);
159 if (firstChar != '0') {
160 hexadecimal = false;
161 } else if (firstChar < '0' && firstChar > '9'){
162 return true;
164 final char secondChar = text.charAt(1);
165 if (hexadecimal) {
166 if (secondChar != 'x' && secondChar != 'X') {
167 hexadecimal = false;
170 int index = hexadecimal ? 2 : 1;
171 char nextChar = text.charAt(index);
172 if (hexadecimal && (nextChar < '0' || nextChar > '9')) {
173 return true;
175 while (nextChar >= '0' && nextChar <='9') {
176 index++;
177 if (index >= length) {
178 return true;
180 nextChar = text.charAt(index);
182 if (nextChar != '.') {
183 return true;
185 index++;
186 if (index >= length) {
187 return true;
189 nextChar = text.charAt(index);
190 if (nextChar < '0' || nextChar > '9') {
191 return true;
193 while (nextChar >= '0' && nextChar <= '9') {
194 index++;
195 if (index >= length) {
196 return hexadecimal;
198 nextChar = text.charAt(index);
200 if (hexadecimal) {
201 if (nextChar != 'p' && nextChar != 'P') {
202 return true;
204 } else {
205 if (nextChar != 'e' && nextChar != 'E') {
206 if (nextChar =='f' || nextChar == 'F' ||
207 nextChar == 'd' || nextChar == 'D') {
208 if (index == length - 1) {
209 return false;
212 return true;
215 index++;
216 if (index >= length) {
217 return true;
219 nextChar = text.charAt(index);
220 if (nextChar == '-') {
221 index++;
222 if (index >= length) {
223 return true;
225 nextChar = text.charAt(index);
227 while (nextChar >= '0' && nextChar <= '9') {
228 index++;
229 if (index >= length) {
230 return false;
232 nextChar = text.charAt(index);
234 // ignore trailing f, F, d or D
235 return false;