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

com.intellij.refactoring.typeMigration.TypeMigrationStatementProcessor Maven / Gradle / Ivy

Go to download

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

The newest version!
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * 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.intellij.refactoring.typeMigration;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.Map;

/**
 * @author anna
 * Date: 04-Apr-2008
 */
class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
  private final PsiElement myStatement;
  private final TypeMigrationLabeler myLabeler;
  private static final Logger LOG = Logger.getInstance("#" + TypeMigrationStatementProcessor.class.getName());
  private final TypeEvaluator myTypeEvaluator;

  public TypeMigrationStatementProcessor(final PsiElement expression, TypeMigrationLabeler labeler) {
    myStatement = expression;
    myLabeler = labeler;
    myTypeEvaluator = myLabeler.getTypeEvaluator();
  }

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

    final PsiExpression lExpression = expression.getLExpression();
    final TypeView left = new TypeView(lExpression);

    final PsiExpression rExpression = expression.getRExpression();
    if (rExpression == null) return;
    final TypeView right = new TypeView(rExpression);

    final IElementType sign = expression.getOperationTokenType();
    final PsiType ltype = left.getType();
    final PsiType rtype = right.getType();
    if (ltype == null || rtype == null) return;

    if (sign != JavaTokenType.EQ) {
      final IElementType binaryOperator = TypeConversionUtil.convertEQtoOperation(sign);
      if (!TypeConversionUtil.isBinaryOperatorApplicable(binaryOperator, ltype, rtype, false)) {
        if (left.isChanged()) {
          findConversionOrFail(expression, lExpression, left.getTypePair());
        }
        if (right.isChanged()) {
          findConversionOrFail(expression, rExpression, right.getTypePair());
        }
        return;
      }
    }

    switch (TypeInfection.getInfection(left, right)) {
      case TypeInfection.NONE_INFECTED:
        break;

      case TypeInfection.LEFT_INFECTED:
        myLabeler.migrateExpressionType(rExpression, ltype, myStatement, TypeConversionUtil.isAssignable(ltype, rtype), true);
        break;

      case TypeInfection.RIGHT_INFECTED:
        myLabeler.migrateExpressionType(lExpression, rtype, myStatement, TypeConversionUtil.isAssignable(ltype, rtype), false);
        break;

      case TypeInfection.BOTH_INFECTED:
        addTypeUsage(lExpression);
        addTypeUsage(rExpression);
        break;

      default:
        LOG.error("Must not happen.");
    }
  }

  @Override
  public void visitArrayAccessExpression(final PsiArrayAccessExpression expression) {
    super.visitArrayAccessExpression(expression);
    final PsiExpression indexExpression = expression.getIndexExpression();
    if (indexExpression != null) {
      checkIndexExpression(indexExpression);
    }
    final TypeView typeView = new TypeView(expression.getArrayExpression());
    if (typeView.isChanged() && typeView.getType() instanceof PsiClassType) {
      final TypeConversionDescriptorBase conversion =
        myLabeler.getRules().findConversion(typeView.getTypePair().first, typeView.getType(), null, expression, false, myLabeler);

      if (conversion == null) {
        myLabeler.markFailedConversion(typeView.getTypePair(), expression);
      }
      else {
        myLabeler.setConversionMapping(expression, conversion);
        myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), myTypeEvaluator.evaluateType(expression));
      }

    }
  }

  @Override
  public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) {
    super.visitSwitchLabelStatement(statement);
    final PsiExpression caseValue = statement.getCaseValue();
    if (caseValue != null) {
      final TypeView typeView = new TypeView(caseValue);
      if (typeView.isChanged()) {
        final PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
        if (switchStatement != null) {
          final PsiExpression expression = switchStatement.getExpression();
          myLabeler.migrateExpressionType(expression, typeView.getType(), myStatement, false, false);
        }
      }
    }
  }

  @Override
  public void visitInstanceOfExpression(final PsiInstanceOfExpression expression) {
    super.visitInstanceOfExpression(expression);
    final PsiTypeElement typeElement = expression.getCheckType();
    if (typeElement != null) {
      final PsiExpression consideredExpression = expression.getOperand();
      final PsiType migrationType = myTypeEvaluator.evaluateType(consideredExpression);
      final PsiType fixedType = typeElement.getType();
      if (migrationType != null && !TypeConversionUtil.isAssignable(migrationType, fixedType)) {
        myLabeler.markFailedConversion(Pair.create(fixedType, migrationType), consideredExpression);
      }
    }
  }

  @Override
  public void visitTypeCastExpression(final PsiTypeCastExpression expression) {
    super.visitTypeCastExpression(expression);
    final PsiTypeElement typeElement = expression.getCastType();
    if (typeElement != null) {
      final PsiType fixedType = typeElement.getType();
      final PsiType migrationType = myTypeEvaluator.evaluateType(expression.getOperand());
      if (migrationType != null && !TypeConversionUtil.areTypesConvertible(migrationType, fixedType)) {
        myLabeler.markFailedConversion(Pair.create(fixedType, migrationType), expression);
      }
    }
  }

  @Override
  public void visitVariable(PsiVariable variable) {
    super.visitVariable(variable);

    final PsiExpression initializer = variable.getInitializer();

    if (initializer != null && initializer.getType() != null) {
      processVariable(variable, initializer, null, null, null, false);
    }
  }

  @Override
  public void visitReturnStatement(PsiReturnStatement statement) { // has to change method return type corresponding to new value type 
    super.visitReturnStatement(statement);

    final PsiElement method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, PsiLambdaExpression.class);
    final PsiExpression value = statement.getReturnValue();

    if (method != null && value != null) {
      if (method instanceof PsiLambdaExpression) {
        //todo [IDEA-133097]
        return;
      }
      final PsiType returnType = ((PsiMethod)method).getReturnType();
      final PsiType valueType = myTypeEvaluator.evaluateType(value);

      if (returnType != null && valueType != null) {
        if (!myLabeler.addMigrationRoot(method, valueType, myStatement, TypeConversionUtil.isAssignable(returnType, valueType), true)
            && TypeMigrationLabeler.typeContainsTypeParameters(returnType)) {
          myLabeler.markFailedConversion(Pair.create(returnType, valueType), value);
        }
      }
    }
  }

  @Override
  public void visitReferenceExpression(PsiReferenceExpression expression) {
    final PsiExpression qualifierExpression = expression.getQualifierExpression();

    if (qualifierExpression != null && qualifierExpression.isPhysical()) {
      qualifierExpression.accept(this);

      final TypeView qualifierView = new TypeView(qualifierExpression);

      if (qualifierView.isChanged()) {
        final PsiMember member = (PsiMember)expression.advancedResolve(false).getElement();
        if (member == null) return;
        final Pair typePair = qualifierView.getTypePair();

        final TypeConversionDescriptorBase conversion = myLabeler.getRules().findConversion(typePair.getFirst(), typePair.getSecond(), member, expression, false, myLabeler);

        if (conversion == null) {
          myLabeler.markFailedConversion(typePair, qualifierExpression);
        } else {
          final PsiElement parent = Util.getEssentialParent(expression);
          if (parent instanceof PsiMethodCallExpression) {
            myLabeler.setConversionMapping((PsiMethodCallExpression)parent, conversion);
            myTypeEvaluator.setType(new TypeMigrationUsageInfo(parent), myTypeEvaluator.evaluateType((PsiExpression)parent));
          } else {
            myLabeler.setConversionMapping(expression, conversion);
            myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), myTypeEvaluator.evaluateType(expression));
          }
        }
      }
    }
  }

  @Override
  public void visitIfStatement(PsiIfStatement statement) {
    super.visitIfStatement(statement);
    final PsiExpression condition = statement.getCondition();
    if (condition != null) {
      final TypeView view = new TypeView(condition);
      if (view.isChanged()) { //means that boolean condition becomes non-boolean
        findConversionOrFail(condition, condition, view.getTypePair());
      }
    }
  }

  @Override
  public void visitForeachStatement(final PsiForeachStatement statement) {
    super.visitForeachStatement(statement);
    final PsiExpression value = statement.getIteratedValue();
    final PsiParameter psiParameter = statement.getIterationParameter();
    if (value != null) {
      final TypeView typeView = new TypeView(value);
      PsiType psiType = typeView.getType();
      if (psiType instanceof PsiArrayType) {
        psiType = ((PsiArrayType)psiType).getComponentType();
      }
      else if (psiType instanceof PsiClassType) {
        final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)psiType).resolveGenerics();
        final PsiClass psiClass = resolveResult.getElement();
        final PsiType targetTypeParameter = getTargetTypeParameter(psiClass, value, typeView);
        if (targetTypeParameter == null) return;
        psiType = resolveResult.getSubstitutor().substitute(targetTypeParameter);
        if (psiType instanceof PsiWildcardType) {
          psiType = ((PsiWildcardType)psiType).getExtendsBound();
        }
      }
      else {
        return;
      }
      final TypeView left = new TypeView(psiParameter, null, null);
      if (TypeInfection.getInfection(left, typeView) == TypeInfection.LEFT_INFECTED) {
        PsiType iterableType;
        final PsiType typeViewType = typeView.getType();
        if (typeViewType instanceof PsiArrayType) {
          iterableType = left.getType().createArrayType();
        } else {
          final PsiClass iterableClass = PsiUtil.resolveClassInType(typeViewType);
          LOG.assertTrue(iterableClass != null);

          final PsiType targetType = getTargetTypeParameter(iterableClass, value, typeView);
          final PsiClass typeParam = PsiUtil.resolveClassInClassTypeOnly(targetType);
          if (!(typeParam instanceof PsiTypeParameter)) return;

          final Map substMap = Collections.singletonMap(((PsiTypeParameter)typeParam), left.getType());

          final PsiElementFactory factory = JavaPsiFacade.getElementFactory(iterableClass.getProject());
          iterableType = factory.createType(iterableClass, factory.createSubstitutor(substMap));
        }
        myLabeler.migrateExpressionType(value, iterableType, myStatement, TypeConversionUtil.isAssignable(iterableType, typeViewType), true);
      } else {
        processVariable(psiParameter, value, psiType, null, null, false);
      }
    }
  }

  private PsiType getTargetTypeParameter(PsiClass iterableClass, PsiExpression value, TypeView typeView) {
    final Project project = iterableClass.getProject();
    final PsiClass itClass =
      JavaPsiFacade.getInstance(project).findClass("java.lang.Iterable", GlobalSearchScope.allScope(project));
    if (itClass == null) return null;

    if (!InheritanceUtil.isInheritorOrSelf(iterableClass, itClass, true)) {
      findConversionOrFail(value, value, typeView.getTypePair());
      return null;
    }

    final PsiSubstitutor aSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(itClass, iterableClass, PsiSubstitutor.EMPTY);

    return aSubstitutor.substitute(itClass.getTypeParameters()[0]);
  }
  
  @Override
  public void visitNewExpression(final PsiNewExpression expression) {
    super.visitNewExpression(expression);
    final PsiExpression[] dimensions = expression.getArrayDimensions();
    for (PsiExpression dimension : dimensions) {
      checkIndexExpression(dimension);
    }
    final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
    if (arrayInitializer != null) {
      processArrayInitializer(arrayInitializer, expression);
    }
  }

  @Override
  public void visitArrayInitializerExpression(final PsiArrayInitializerExpression expression) {
    super.visitArrayInitializerExpression(expression);
    processArrayInitializer(expression, expression);
  }

  @Override
  public void visitPostfixExpression(final PsiPostfixExpression expression) {
    super.visitPostfixExpression(expression);
    processUnaryExpression(expression, expression.getOperationSign());
  }

  @Override
  public void visitPrefixExpression(final PsiPrefixExpression expression) {
    super.visitPrefixExpression(expression);
    processUnaryExpression(expression, expression.getOperationSign());
  }

  private void processUnaryExpression(final PsiExpression expression, PsiJavaToken sign) {
    final TypeView typeView = new TypeView(expression);
    if (typeView.isChanged()) {
      if (!TypeConversionUtil.isUnaryOperatorApplicable(sign, typeView.getType())) {
        findConversionOrFail(expression, expression, typeView.getTypePair());
      }
    }
  }

  private void findConversionOrFail(PsiExpression expression, PsiExpression toFail, Pair typePair) {
    final TypeConversionDescriptorBase conversion = myLabeler.getRules().findConversion(typePair.getFirst(), typePair.getSecond(), null, expression, myLabeler);
    if (conversion == null) {
      myLabeler.markFailedConversion(typePair, toFail);
    }
    else {
      myLabeler.setConversionMapping(expression, conversion);
      final PsiType psiType = myTypeEvaluator.evaluateType(expression);
      LOG.assertTrue(psiType != null,  expression);
      myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), psiType);
    }
  }

  @Override
  public void visitPolyadicExpression(PsiPolyadicExpression expression) {
    super.visitPolyadicExpression(expression);
    final PsiExpression[] operands = expression.getOperands();
    if (operands.length == 0) return;
    final IElementType operationTokenType = expression.getOperationTokenType();

    PsiExpression lOperand = operands[0];
    TypeView left = new TypeView(lOperand);
    for(int i = 1; i < operands.length; i++) {
      final PsiExpression rOperand = operands[i];
      if (rOperand == null) return;
      final TypeView right = new TypeView(rOperand);
      if (!TypeConversionUtil.isBinaryOperatorApplicable(operationTokenType, left.getType(), right.getType(), false)) {
        if (left.isChanged()) {
          findConversionOrFail(expression, lOperand, left.getTypePair());
        }
        if (right.isChanged()) {
          findConversionOrFail(expression, rOperand, right.getTypePair());
        }
      }
      lOperand = rOperand;
      left = right;
    }
  }

  private void processArrayInitializer(final PsiArrayInitializerExpression expression, final PsiExpression parentExpression) {
    final PsiExpression[] initializers = expression.getInitializers();
    PsiType migrationType = null;
    for (PsiExpression initializer : initializers) {
      final TypeView typeView = new TypeView(initializer);
      if (typeView.isChanged()) {
        final PsiType type = typeView.getType();
        if (migrationType == null || !TypeConversionUtil.isAssignable(migrationType, type)) {
          if (migrationType != null && !TypeConversionUtil.isAssignable(type, migrationType)) {
            myLabeler.markFailedConversion(Pair.create(parentExpression.getType(), type), parentExpression);
            return;
          }
          migrationType = type;
        }
      }
    }
    final PsiType exprType = expression.getType();
    if (migrationType != null && exprType instanceof PsiArrayType) {
      final boolean alreadyProcessed = TypeConversionUtil.isAssignable(((PsiArrayType)exprType).getComponentType(), migrationType);
      myLabeler.migrateExpressionType(parentExpression, alreadyProcessed ? exprType : migrationType.createArrayType(), expression, alreadyProcessed, true);
    }
  }

  private void checkIndexExpression(final PsiExpression indexExpression) {
    final PsiType indexType = myTypeEvaluator.evaluateType(indexExpression);
    if (indexType != null && !TypeConversionUtil.isAssignable(PsiType.INT, indexType)) {
      myLabeler.markFailedConversion(Pair.create(indexExpression.getType(), indexType), indexExpression);
    }
  }

  @Override
  public void visitMethodCallExpression(final PsiMethodCallExpression methodCallExpression) {
    super.visitMethodCallExpression(methodCallExpression);
    final JavaResolveResult resolveResult = methodCallExpression.resolveMethodGenerics();
    final PsiElement method = resolveResult.getElement();
    if (method instanceof PsiMethod) {
      final PsiExpression[] psiExpressions = methodCallExpression.getArgumentList().getExpressions();
      final PsiParameter[] originalParams = ((PsiMethod)method).getParameterList().getParameters();
      final PsiSubstitutor evalSubstitutor = myTypeEvaluator.createMethodSubstitution(originalParams, psiExpressions, (PsiMethod)method, methodCallExpression);
      for (int i = 0; i < psiExpressions.length; i++) {
        PsiParameter originalParameter;
        if (originalParams.length <= i) {
          if (originalParams.length > 0 && originalParams[originalParams.length - 1].isVarArgs()) {
            originalParameter = originalParams[originalParams.length - 1];
          } else {
            continue;
          }
        }
        else {
          originalParameter = originalParams[i];
        }
        processVariable(originalParameter, psiExpressions[i], null, resolveResult.getSubstitutor(), evalSubstitutor, true);
      }
      final PsiExpression qualifier = methodCallExpression.getMethodExpression().getQualifierExpression();
      if (qualifier != null && qualifier.isPhysical() && !new TypeView(qualifier).isChanged()) { //substitute property otherwise
        final PsiType qualifierType = qualifier.getType();
        if (qualifierType instanceof PsiClassType) {
          final PsiClassType.ClassResolveResult classResolveResult = ((PsiClassType)qualifierType).resolveGenerics();
          final PsiType migrationType =
              classResolveResult.getSubstitutor().substitute(evalSubstitutor.substitute(JavaPsiFacade.getElementFactory(myStatement.getProject()).createType(classResolveResult.getElement(), PsiSubstitutor.EMPTY)));
          myLabeler.migrateExpressionType(qualifier, migrationType, myStatement, migrationType.equals(qualifierType), true);
        }
      }
    }
  }

  private void processVariable(final PsiVariable variable,
                               final PsiExpression value,
                               final PsiType migrationType,
                               final PsiSubstitutor varSubstitutor,
                               final PsiSubstitutor evalSubstitutor,
                               final boolean isCovariantPosition) {
    final TypeView right = new TypeView(value);
    final TypeView left = new TypeView(variable, varSubstitutor, evalSubstitutor);
    final PsiType declarationType = left.getType();

    switch (TypeInfection.getInfection(left, right)) {
      case TypeInfection.NONE_INFECTED:
        break;

      case TypeInfection.LEFT_INFECTED:
        final PsiType valueType = right.getType();
        if (valueType != null && declarationType != null) {
          myLabeler.migrateExpressionType(value, declarationType, myStatement, TypeConversionUtil.isAssignable(declarationType, valueType), true);
        }
        break;

      case TypeInfection.RIGHT_INFECTED:
        PsiType psiType = migrationType != null ? migrationType : right.getType();
        if (psiType != null && declarationType != null && 
            !myLabeler.addMigrationRoot(variable, psiType, myStatement, TypeConversionUtil.isAssignable(declarationType, psiType), true) && 
            !TypeConversionUtil.isAssignable(left.getType(), psiType)) {
          myLabeler.convertExpression(value, psiType, left.getType(), isCovariantPosition);
        }
        break;

      case TypeInfection.BOTH_INFECTED:
        addTypeUsage(variable);
        break;

      default:
        LOG.error("Must not happen.");
    }
  }


  private void addTypeUsage(final PsiElement typedElement) {
    if (typedElement instanceof PsiReferenceExpression) {
      myLabeler.setTypeUsage(((PsiReferenceExpression)typedElement).resolve(), myStatement);
    }
    else if (typedElement instanceof PsiMethodCallExpression) {
      myLabeler.setTypeUsage(((PsiMethodCallExpression)typedElement).resolveMethod(), myStatement);
    }
    else {
      myLabeler.setTypeUsage(typedElement, myStatement);
    }
  }


  private class TypeView {
    final PsiType myOriginType;
    final PsiType myType;
    final boolean myChanged;

    public TypeView(@NotNull PsiExpression expr) {
      PsiType exprType = expr.getType();
      exprType = exprType instanceof PsiEllipsisType ? ((PsiEllipsisType)exprType).toArrayType() : exprType;
      myOriginType = GenericsUtil.getVariableTypeByExpressionType(exprType);
      PsiType type = myTypeEvaluator.evaluateType(expr);
      type = type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type;
      myType = GenericsUtil.getVariableTypeByExpressionType(type);
      myChanged = (myOriginType == null || myType == null) ? false : !myType.equals(myOriginType);
    }

    public TypeView(PsiVariable var, PsiSubstitutor varSubstitutor, PsiSubstitutor evalSubstitutor) {
      myOriginType = varSubstitutor != null ? varSubstitutor.substitute(var.getType()) : var.getType();
      myType = evalSubstitutor != null
               ? evalSubstitutor.substitute(myTypeEvaluator.getType(var))
               : myTypeEvaluator.getType(var);
      myChanged = (myOriginType == null || myType == null) ? false : !myType.equals(myOriginType);
    }

    public PsiType getType() {
      return myType;
    }

    public boolean isChanged() {
      return myChanged;
    }

    public Pair getTypePair() {
      return Pair.create(myOriginType, myType);
    }
  }

  private static class TypeInfection {
    static final int NONE_INFECTED = 0;
    static final int LEFT_INFECTED = 1;
    static final int RIGHT_INFECTED = 2;
    static final int BOTH_INFECTED = 3;

    static int getInfection(final TypeView left, final TypeView right) {
      return (left.isChanged() ? 1 : 0) + (right.isChanged() ? 2 : 0);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy