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

com.siyeh.ig.jdk.AutoUnboxingInspection 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-2015 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.jdk;

import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Query;
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.MethodCallUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class AutoUnboxingInspection extends BaseInspection {

  /**
   * @noinspection StaticCollection
   */
  @NonNls static final Map s_unboxingMethods = new HashMap(8);

  static {
    s_unboxingMethods.put("byte", "byteValue");
    s_unboxingMethods.put("short", "shortValue");
    s_unboxingMethods.put("int", "intValue");
    s_unboxingMethods.put("long", "longValue");
    s_unboxingMethods.put("float", "floatValue");
    s_unboxingMethods.put("double", "doubleValue");
    s_unboxingMethods.put("boolean", "booleanValue");
    s_unboxingMethods.put("char", "charValue");
  }

  @Override
  @NotNull
  public String getDisplayName() {
    return InspectionGadgetsBundle.message("auto.unboxing.display.name");
  }

  @Override
  @NotNull
  public String buildErrorString(Object... infos) {
    return InspectionGadgetsBundle.message("auto.unboxing.problem.descriptor");
  }

  @Override
  @Nullable
  public InspectionGadgetsFix buildFix(Object... infos) {
    if (infos.length == 0 || !isFixApplicable((PsiExpression)infos[0])) {
      return null;
    }
    return new AutoUnboxingFix();
  }

  private static boolean isFixApplicable(PsiExpression location) {
    // conservative check to see if the result value of the postfix
    // expression is used later in the same expression statement.
    // Applying the quick fix in such a case would break the code
    // because the explicit unboxing code would split the expression in
    // multiple statements.
    final PsiElement parent = location.getParent();
    if (!(parent instanceof PsiPostfixExpression)) {
      return true;
    }
    final PsiReferenceExpression reference;
    if (location instanceof PsiReferenceExpression) {
      reference = (PsiReferenceExpression)location;
    }
    else if (location instanceof PsiArrayAccessExpression) {
      final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)location;
      final PsiExpression expression = arrayAccessExpression.getArrayExpression();
      if (!(expression instanceof PsiReferenceExpression)) {
        return true;
      }
      reference = (PsiReferenceExpression)expression;
    }
    else {
      return true;
    }
    final PsiElement element = reference.resolve();
    if (element == null) {
      return true;
    }
    final PsiStatement statement = PsiTreeUtil.getParentOfType(parent, PsiStatement.class);
    final LocalSearchScope scope = new LocalSearchScope(statement);
    final Query query = ReferencesSearch.search(element, scope);
    final Collection references = query.findAll();
    return references.size() <= 1;
  }

  private static class AutoUnboxingFix extends InspectionGadgetsFix {

    @Override
    @NotNull
    public String getName() {
      return InspectionGadgetsBundle.message("auto.unboxing.make.unboxing.explicit.quickfix");
    }
    @Override
    @NotNull
    public String getFamilyName() {
      return getName();
    }

    @Override
    public void doFix(Project project, ProblemDescriptor descriptor) {
      final PsiExpression expression = (PsiExpression)descriptor.getPsiElement();
      final PsiType type = expression.getType();
      if (type == null) {
        return;
      }
      final PsiPrimitiveType unboxedType = (PsiPrimitiveType)ExpectedTypeUtils.findExpectedType(expression, false, true);
      if (unboxedType == null) {
        return;
      }
      final String newExpressionText = buildNewExpressionText(expression, unboxedType);
      final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
      final PsiElementFactory factory = psiFacade.getElementFactory();
      final PsiElement parent = expression.getParent();
      final String expressionText = expression.getText();
      if (parent instanceof PsiTypeCastExpression) {
        final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)parent;
        PsiReplacementUtil.replaceExpression(typeCastExpression, newExpressionText);
      }
      else if (parent instanceof PsiPrefixExpression && !unboxedType.equalsToText("boolean")) {
        final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)parent;
        final IElementType tokenType = prefixExpression.getOperationTokenType();
        if (JavaTokenType.PLUSPLUS.equals(tokenType)) {
          PsiReplacementUtil.replaceExpression(prefixExpression, expressionText + '=' + newExpressionText + "+1");
        }
        else if (JavaTokenType.MINUSMINUS.equals(tokenType)) {
          PsiReplacementUtil.replaceExpression(prefixExpression, expressionText + '=' + newExpressionText + "-1");
        } else {
          PsiReplacementUtil.replaceExpression(prefixExpression, prefixExpression.getOperationSign().getText() + newExpressionText);
        }
      }
      else if (parent instanceof PsiPostfixExpression) {
        final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)parent;
        final IElementType tokenType = postfixExpression.getOperationTokenType();
        final PsiElement grandParent = postfixExpression.getParent();
        if (grandParent instanceof PsiExpressionStatement) {
          if (JavaTokenType.PLUSPLUS.equals(tokenType)) {
            PsiReplacementUtil.replaceExpression(postfixExpression, expressionText + '=' + newExpressionText + "+1");
          }
          else if (JavaTokenType.MINUSMINUS.equals(tokenType)) {
            PsiReplacementUtil.replaceExpression(postfixExpression, expressionText + '=' + newExpressionText + "-1");
          }
        }
        else {
          final PsiElement element = postfixExpression.replace(postfixExpression.getOperand());
          final PsiStatement statement = PsiTreeUtil.getParentOfType(element, PsiStatement.class);
          if (statement == null) {
            return;
          }
          final PsiStatement newStatement;
          if (JavaTokenType.PLUSPLUS.equals(tokenType)) {
            newStatement = factory.createStatementFromText(expressionText + '=' + newExpressionText + "+1;", statement);
          }
          else {
            newStatement = factory.createStatementFromText(expressionText + '=' + newExpressionText + "-1;", statement);
          }
          final PsiElement greatGrandParent = statement.getParent();
          greatGrandParent.addAfter(newStatement, statement);
        }
      }
      else if (parent instanceof PsiAssignmentExpression) {
        final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
        final PsiExpression lExpression = assignmentExpression.getLExpression();
        if (expression.equals(lExpression)) {
          final PsiJavaToken operationSign = assignmentExpression.getOperationSign();
          final String operationSignText = operationSign.getText();
          final char sign = operationSignText.charAt(0);
          final PsiExpression rExpression = assignmentExpression.getRExpression();
          if (rExpression == null) {
            return;
          }
          final String text = lExpression.getText() + '=' + newExpressionText + sign + rExpression.getText();
          final PsiExpression newExpression = factory.createExpressionFromText(text, assignmentExpression);
          assignmentExpression.replace(newExpression);
        }
        else {
          PsiReplacementUtil.replaceExpression(expression, newExpressionText);
        }
      }
      else {
        PsiReplacementUtil.replaceExpression(expression, newExpressionText);
      }
    }

    private static String buildNewExpressionText(PsiExpression expression, PsiPrimitiveType unboxedType) {
      final String unboxedTypeText = unboxedType.getCanonicalText();
      final String expressionText = expression.getText();
      final String boxMethodName = s_unboxingMethods.get(unboxedTypeText);
      if (expression instanceof PsiTypeCastExpression) {
        return '(' + expressionText + ")." + boxMethodName + "()";
      }
      final String constantText = computeConstantBooleanText(expression);
      if (constantText != null) {
        return constantText;
      }
      if (expression instanceof PsiMethodCallExpression) {
        final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
        if (isValueOfCall(methodCallExpression)) {
          final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
          final PsiExpression[] arguments = argumentList.getExpressions();
          final PsiExpression argument = arguments[0];
          return argument.getText();
        }
      }
      final PsiType type = expression.getType();
      if (type != null && type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
        return "((" + unboxedType.getBoxedTypeName() + ')' + expressionText + ")." + boxMethodName + "()";
      }
      return expressionText + '.' + boxMethodName + "()";
    }

    private static boolean isValueOfCall(PsiMethodCallExpression methodCallExpression) {
      final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
      final PsiExpression[] arguments = argumentList.getExpressions();
      if (arguments.length != 1) {
        return false;
      }
      final PsiExpression argument = arguments[0];
      final PsiType type = argument.getType();
      return (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_INTEGER, null, "valueOf", PsiType.INT) &&
              PsiType.INT.equals(type)) ||
             (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_SHORT, null, "valueOf", PsiType.SHORT) &&
              PsiType.SHORT.equals(type)) ||
             (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_BYTE, null, "valueOf", PsiType.BYTE) &&
              PsiType.BYTE.equals(type)) ||
             (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_LONG, null, "valueOf", PsiType.LONG) &&
              PsiType.LONG.equals(type)) ||
             (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_CHARACTER, null, "valueOf", PsiType.CHAR) &&
              PsiType.CHAR.equals(type)) ||
             (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_DOUBLE, null, "valueOf", PsiType.DOUBLE) &&
              PsiType.DOUBLE.equals(type)) ||
             (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_FLOAT, null, "valueOf", PsiType.FLOAT) &&
              PsiType.FLOAT.equals(type));
    }

    @NonNls
    private static String computeConstantBooleanText(PsiExpression expression) {
      if (!(expression instanceof PsiReferenceExpression)) {
        return null;
      }
      final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression;
      final PsiElement target = referenceExpression.resolve();
      if (!(target instanceof PsiField)) {
        return null;
      }
      final PsiField field = (PsiField)target;
      final PsiClass containingClass = field.getContainingClass();
      if (containingClass == null) {
        return null;
      }
      final String qualifiedName = containingClass.getQualifiedName();
      if (!CommonClassNames.JAVA_LANG_BOOLEAN.equals(qualifiedName)) {
        return null;
      }
      @NonNls final String name = field.getName();
      if ("TRUE".equals(name)) {
        return "true";
      }
      else if ("FALSE".equals(name)) {
        return "false";
      }
      else {
        return null;
      }
    }
  }

  @Override
  public boolean shouldInspect(PsiFile file) {
    return PsiUtil.isLanguageLevel5OrHigher(file);
  }

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

  private static class AutoUnboxingVisitor extends BaseInspectionVisitor {
    @Override
    public void visitArrayAccessExpression(PsiArrayAccessExpression expression) {
      super.visitArrayAccessExpression(expression);
      checkExpression(expression);
    }

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

    @Override
    public void visitReferenceExpression(PsiReferenceExpression expression) {
      super.visitReferenceExpression(expression);
      if (expression instanceof PsiMethodReferenceExpression) {
        final PsiMethodReferenceExpression methodReferenceExpression = (PsiMethodReferenceExpression)expression;
        if (methodReferenceExpression.isConstructor()) {
          return;
        }
        final PsiElement referenceNameElement = methodReferenceExpression.getReferenceNameElement();
        if (referenceNameElement == null) {
          return;
        }
        final PsiElement target = methodReferenceExpression.resolve();
        if (!(target instanceof PsiMethod)) {
          return;
        }
        final PsiMethod method = (PsiMethod)target;
        final PsiType returnType = method.getReturnType();
        if (!TypeConversionUtil.isAssignableFromPrimitiveWrapper(returnType)) {
          return;
        }
        final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(returnType);
        if (unboxedType == null) {
          return;
        }
        final PsiType functionalInterfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(methodReferenceExpression);
        if (functionalInterfaceReturnType == null || !ClassUtils.isPrimitive(functionalInterfaceReturnType) ||
            !functionalInterfaceReturnType.isAssignableFrom(unboxedType)) {
          return;
        }
        registerError(referenceNameElement);
      }
      else {
        checkExpression(expression);
      }
    }

    @Override
    public void visitNewExpression(PsiNewExpression expression) {
      super.visitNewExpression(expression);
      checkExpression(expression);
    }

    @Override
    public void visitMethodCallExpression(PsiMethodCallExpression expression) {
      super.visitMethodCallExpression(expression);
      final PsiMethod method = expression.resolveMethod();
      if (method != null &&
          AnnotationUtil.isAnnotated(method, Collections.singletonList( "java.lang.invoke.MethodHandle.PolymorphicSignature"))) {
        return;
      }
      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) {
      if (expression.getParent() instanceof PsiParenthesizedExpression) {
        return;
      }
      final PsiType expressionType = expression.getType();
      if (!TypeConversionUtil.isAssignableFromPrimitiveWrapper(expressionType)) {
        return;
      }
      final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false, true);
      if (!TypeConversionUtil.isPrimitiveAndNotNull(expectedType)) {
        return;
      }
      if (!(expression.getParent() instanceof PsiTypeCastExpression)) {
        final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(expressionType);
        if (unboxedType == null || !expectedType.isAssignableFrom(unboxedType)) {
          return;
        }
      }
      registerError(expression, expression);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy