All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.siyeh.ig.numeric.ImplicitNumericConversionInspection Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition java-analysis-impl library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.siyeh.ig.numeric;

import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.HardcodedMethodConstants;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.PsiReplacementUtil;
import com.siyeh.ig.psiutils.ClassUtils;
import com.siyeh.ig.psiutils.ExpectedTypeUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;

public class ImplicitNumericConversionInspection extends BaseInspection {

  /**
   * @noinspection StaticCollection
   */
  private static final gnu.trove.TObjectIntHashMap typePrecisions = new TObjectIntHashMap(7);

  static {
    typePrecisions.put(PsiType.BYTE, 1);
    typePrecisions.put(PsiType.CHAR, 2);
    typePrecisions.put(PsiType.SHORT, 2);
    typePrecisions.put(PsiType.INT, 3);
    typePrecisions.put(PsiType.LONG, 4);
    typePrecisions.put(PsiType.FLOAT, 5);
    typePrecisions.put(PsiType.DOUBLE, 6);
  }

  @SuppressWarnings({"PublicField"})
  public boolean ignoreWideningConversions = false;

  @SuppressWarnings({"PublicField"})
  public boolean ignoreCharConversions = false;

  @SuppressWarnings({"PublicField"})
  public boolean ignoreConstantConversions = false;

  @Override
  @NotNull
  public String getDisplayName() {
    return InspectionGadgetsBundle.message("implicit.numeric.conversion.display.name");
  }

  @Override
  public JComponent createOptionsPanel() {
    final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this);
    optionsPanel.addCheckbox(InspectionGadgetsBundle.message("implicit.numeric.conversion.ignore.widening.conversion.option"),
                             "ignoreWideningConversions");
    optionsPanel.addCheckbox(InspectionGadgetsBundle.message("implicit.numeric.conversion.ignore.char.conversion.option"),
                             "ignoreCharConversions");
    optionsPanel.addCheckbox(InspectionGadgetsBundle.message("implicit.numeric.conversion.ignore.constant.conversion.option"),
                             "ignoreConstantConversions");
    return optionsPanel;
  }

  @Override
  @NotNull
  public String buildErrorString(Object... infos) {
    final PsiType type = (PsiType)infos[1];
    final PsiType expectedType = (PsiType)infos[2];
    return InspectionGadgetsBundle.message("implicit.numeric.conversion.problem.descriptor",
      type.getPresentableText(), expectedType.getPresentableText());
  }

  @Override
  public BaseInspectionVisitor buildVisitor() {
    return new ImplicitNumericConversionVisitor();
  }

  @Override
  public InspectionGadgetsFix buildFix(Object... infos) {
    return new ImplicitNumericConversionFix((PsiExpression)infos[0], (PsiType)infos[2]);
  }

  private static class ImplicitNumericConversionFix extends InspectionGadgetsFix {

    private final String m_name;

    ImplicitNumericConversionFix(PsiExpression expression, PsiType expectedType) {
      if (isConvertible(expression, expectedType)) {
        m_name = InspectionGadgetsBundle.message("implicit.numeric.conversion.convert.quickfix", expectedType.getCanonicalText());
      }
      else {
        m_name = InspectionGadgetsBundle.message("implicit.numeric.conversion.make.explicit.quickfix");
      }
    }

    @NotNull
    @Override
    public String getFamilyName() {
      return InspectionGadgetsBundle.message("implicit.numeric.conversion.make.explicit.quickfix");
    }

    @Override
    @NotNull
    public String getName() {
      return m_name;
    }

    @Override
    public void doFix(Project project, ProblemDescriptor descriptor) {
      final PsiExpression expression = (PsiExpression)descriptor.getPsiElement();
      final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, true);
      if (expectedType == null) {
        return;
      }
      if (isConvertible(expression, expectedType)) {
        final String newExpression = convertExpression(expression, expectedType);
        if (newExpression == null) {
          return;
        }
        PsiReplacementUtil.replaceExpression(expression, newExpression);
      }
      else {
        final String newExpression;
        if (ParenthesesUtils.getPrecedence(expression) <= ParenthesesUtils.TYPE_CAST_PRECEDENCE) {
          newExpression = '(' + expectedType.getCanonicalText() + ')' + expression.getText();
        }
        else {
          newExpression = '(' + expectedType.getCanonicalText() + ")(" + expression.getText() + ')';
        }
        PsiReplacementUtil.replaceExpression(expression, newExpression);
      }
    }

    @Nullable
    @NonNls
    private static String convertExpression(PsiExpression expression, PsiType expectedType) {
      final PsiType expressionType = expression.getType();
      if (expressionType == null) {
        return null;
      }
      if (expressionType.equals(PsiType.INT) && expectedType.equals(PsiType.LONG)) {
        return expression.getText() + 'L';
      }
      if (expressionType.equals(PsiType.INT) && expectedType.equals(PsiType.FLOAT)) {
        return expression.getText() + ".0F";
      }
      if (expressionType.equals(PsiType.INT) && expectedType.equals(PsiType.DOUBLE)) {
        return expression.getText() + ".0";
      }
      if (expressionType.equals(PsiType.LONG) && expectedType.equals(PsiType.FLOAT)) {
        final String text = expression.getText();
        final int length = text.length();
        return text.substring(0, length - 1) + ".0F";
      }
      if (expressionType.equals(PsiType.LONG) && expectedType.equals(PsiType.DOUBLE)) {
        final String text = expression.getText();
        final int length = text.length();
        return text.substring(0, length - 1) + ".0";
      }
      if (expressionType.equals(PsiType.DOUBLE) && expectedType.equals(PsiType.FLOAT)) {
        final String text = expression.getText();
        final int length = text.length();
        if (text.charAt(length - 1) == 'd' || text.charAt(length - 1) == 'D') {
          return text.substring(0, length - 1) + 'F';
        }
        else {
          return text + 'F';
        }
      }
      if (expressionType.equals(PsiType.FLOAT) && expectedType.equals(PsiType.DOUBLE)) {
        final String text = expression.getText();
        final int length = text.length();
        return text.substring(0, length - 1);
      }
      return null;   //can't happen
    }

    private static boolean isConvertible(PsiExpression expression, PsiType expectedType) {
      if (!(expression instanceof PsiLiteralExpression) && !isNegatedLiteral(expression)) {
        return false;
      }
      final PsiType expressionType = expression.getType();
      if (expressionType == null) {
        return false;
      }
      if (hasLowerPrecision(expectedType, expressionType)) {
        return false;
      }
      if (isIntegral(expressionType) && isIntegral(expectedType)) {
        return true;
      }
      if (isIntegral(expressionType) && isFloatingPoint(expectedType)) {
        return true;
      }
      return isFloatingPoint(expressionType) && isFloatingPoint(expectedType);
    }

    private static boolean isNegatedLiteral(PsiExpression expression) {
      if (!(expression instanceof PsiPrefixExpression)) {
        return false;
      }
      final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression;
      final IElementType tokenType = prefixExpression.getOperationTokenType();
      if (!JavaTokenType.MINUS.equals(tokenType)) {
        return false;
      }
      final PsiExpression operand = prefixExpression.getOperand();
      return operand instanceof PsiLiteralExpression;
    }

    private static boolean isIntegral(@Nullable PsiType expressionType) {
      return PsiType.INT.equals(expressionType) || PsiType.LONG.equals(expressionType);
    }

    private static boolean isFloatingPoint(@Nullable PsiType expressionType) {
      return PsiType.FLOAT.equals(expressionType) || PsiType.DOUBLE.equals(expressionType);
    }
  }

  private class ImplicitNumericConversionVisitor extends BaseInspectionVisitor {

    @Override
    public void visitPolyadicExpression(PsiPolyadicExpression expression) {
      super.visitPolyadicExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitConditionalExpression(PsiConditionalExpression expression) {
      super.visitConditionalExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitLiteralExpression(PsiLiteralExpression expression) {
      super.visitLiteralExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitPostfixExpression(PsiPostfixExpression expression) {
      super.visitPostfixExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitPrefixExpression(PsiPrefixExpression expression) {
      super.visitPrefixExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitReferenceExpression(PsiReferenceExpression expression) {
      super.visitReferenceExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitMethodCallExpression(PsiMethodCallExpression expression) {
      super.visitMethodCallExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitTypeCastExpression(PsiTypeCastExpression expression) {
      super.visitTypeCastExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitAssignmentExpression(PsiAssignmentExpression expression) {
      super.visitAssignmentExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
      super.visitParenthesizedExpression(expression);
      checkExpression(expression);
    }

    private void checkExpression(PsiExpression expression) {
      final PsiElement parent = expression.getParent();
      if (parent instanceof PsiParenthesizedExpression) {
        return;
      }
      if (ignoreConstantConversions) {
        PsiExpression rootExpression = expression;
        while (rootExpression instanceof PsiParenthesizedExpression) {
          final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)rootExpression;
          rootExpression = parenthesizedExpression.getExpression();
        }
        if (rootExpression instanceof PsiLiteralExpression || PsiUtil.isConstantExpression(rootExpression)) {
          return;
        }
      }
      final PsiType expressionType = expression.getType();
      if (expressionType == null || !ClassUtils.isPrimitiveNumericType(expressionType)) {
        return;
      }
      if (PsiType.CHAR.equals(expressionType) && (ignoreCharConversions || isArgumentOfStringIndexOf(parent))) {
        return;
      }
      final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, true);
      if (!ClassUtils.isPrimitiveNumericType(expectedType)) {
        return;
      }
      if (expressionType.equals(expectedType)) {
        return;
      }
      if (ignoreWideningConversions && hasLowerPrecision(expressionType, expectedType)) {
        return;
      }
      if (ignoreCharConversions && PsiType.CHAR.equals(expectedType)) {
        return;
      }
      registerError(expression, expression, expressionType, expectedType);
    }

    private boolean isArgumentOfStringIndexOf(PsiElement parent) {
      if (!(parent instanceof PsiExpressionList)) {
        return false;
      }
      final PsiElement grandParent = parent.getParent();
      if (!(grandParent instanceof PsiMethodCallExpression)) {
        return false;
      }
      final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
      final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
      final String methodName = methodExpression.getReferenceName();
      if (!HardcodedMethodConstants.INDEX_OF.equals(methodName) && !HardcodedMethodConstants.LAST_INDEX_OF.equals(methodName)) {
        return false;
      }
      final PsiMethod method = methodCallExpression.resolveMethod();
      if (method == null) {
        return false;
      }
      final PsiClass aClass = method.getContainingClass();
      if (aClass == null) {
        return false;
      }
      final String className = aClass.getQualifiedName();
      return CommonClassNames.JAVA_LANG_STRING.equals(className);
    }
  }

  static boolean hasLowerPrecision(PsiType expressionType, PsiType expectedType) {
    final int operandPrecision = typePrecisions.get(expressionType);
    final int castPrecision = typePrecisions.get(expectedType);
    return operandPrecision <= castPrecision;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy