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

org.jetbrains.plugins.groovy.codeInspection.type.GroovyTypeCheckVisitorHelper Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition groovy-psi 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 org.jetbrains.plugins.groovy.codeInspection.type;

import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.assignment.CallInfo;
import org.jetbrains.plugins.groovy.codeInspection.assignment.ParameterCastFix;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.findUsages.LiteralConstructorReference;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrSpreadArgument;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.spock.SpockUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class GroovyTypeCheckVisitorHelper {
  
  @SuppressWarnings("unchecked") 
  private static final Function id = (Function)Function.ID;
  
  @Nullable
  @Contract("null -> null")
  protected static GrListOrMap getTupleInitializer(@Nullable GrExpression initializer) {
    if (initializer instanceof GrListOrMap &&
        initializer.getReference() instanceof LiteralConstructorReference &&
        ((LiteralConstructorReference)initializer.getReference()).getConstructedClassType() != null) {
      return (GrListOrMap)initializer;
    }
    else {
      return null;
    }
  }

  public static boolean isOnlyOneMapParam(GrExpression[] exprs) {
    if (!(exprs.length == 1)) return false;

    final GrExpression e = exprs[0];
    return TypesUtil.isAssignableByMethodCallConversion(
      TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_UTIL_MAP, e),
      e.getType(),
      e
    );
  }

  @NotNull
  public static PsiElement getExpressionPartToHighlight(@NotNull GrExpression expr) {
    if (expr instanceof GrClosableBlock) {
      final PsiElement highlightElement = ((GrClosableBlock)expr).getLBrace();
      assert highlightElement != null;
      return highlightElement;
    }
    return expr;
  }

  public static boolean checkSimpleArrayAccess(@NotNull CallInfo info, @Nullable PsiType type, @NotNull PsiType[] types) {
    if (!(type instanceof PsiArrayType)) return false;

    if (PsiUtil.isLValue(info.getCall())) {
      if (types.length == 2 &&
          TypesUtil.isAssignable(PsiType.INT, types[0], info.getCall()) &&
          TypesUtil.isAssignable(((PsiArrayType)type).getComponentType(), types[1], info.getCall())) {
        return true;
      }
    }
    else {
      if (types.length == 1 && TypesUtil.isAssignable(PsiType.INT, types[0], info.getCall())) {
        return true;
      }
    }

    return false;
  }

  public static boolean typesAreEqual(@NotNull PsiType expected, @NotNull PsiType actual, @NotNull PsiElement context) {
    return TypesUtil.isAssignableByMethodCallConversion(expected, actual, context) &&
           TypesUtil.isAssignableByMethodCallConversion(actual, expected, context);
  }

  /**
   * checks only children of e
   */
  public static boolean hasErrorElements(@Nullable PsiElement e) {
    if (e == null) return false;

    for (PsiElement child = e.getFirstChild(); child != null; child = child.getNextSibling()) {
      if (child instanceof PsiErrorElement) return true;
    }
    return false;
  }

  public static boolean isSpockTimesOperator(GrBinaryExpression call) {
    if (call.getOperationTokenType() == GroovyTokenTypes.mSTAR && PsiUtil.isExpressionStatement(call)) {
      GrExpression operand = call.getLeftOperand();
      if (operand instanceof GrLiteral && TypesUtil.isNumericType(operand.getType())) {
        PsiClass aClass = PsiUtil.getContextClass(call);
        if (InheritanceUtil.isInheritor(aClass, false, SpockUtils.SPEC_CLASS_NAME)) {
          return true;
        }
      }
    }

    return false;
  }

  public static boolean isOperatorWithSimpleTypes(GrBinaryExpression binary, GroovyResolveResult result) {
    if (result.getElement() != null && result.isApplicable()) {
      return false;
    }

    GrExpression left = binary.getLeftOperand();
    GrExpression right = binary.getRightOperand();

    PsiType ltype = left.getType();
    PsiType rtype = right != null ? right.getType() : null;

    return TypesUtil.isNumericType(ltype) && (rtype == null || TypesUtil.isNumericType(rtype));
  }

  @NotNull
  public static LocalQuickFix[] genCastFixes(@NotNull GrSignature signature,
                                             @NotNull PsiType[] argumentTypes,
                                             @Nullable GrArgumentList argumentList) {
    if (argumentList == null) return LocalQuickFix.EMPTY_ARRAY;
    final List args = getExpressionArgumentsOfCall(argumentList);
    if (args == null) return LocalQuickFix.EMPTY_ARRAY;
    
    final List> allErrors = new ArrayList>();
    final List signatures = GrClosureSignatureUtil.generateSimpleSignatures(signature);
    for (GrClosureSignature closureSignature : signatures) {
      final GrClosureSignatureUtil.MapResultWithError map = GrClosureSignatureUtil.mapSimpleSignatureWithErrors(
        closureSignature, argumentTypes, id, argumentList, 1
      );
      if (map != null) {
        final List> errors = map.getErrors();
        for (Pair error : errors) {
          if (!(error.first == 0 && PsiImplUtil.hasNamedArguments(argumentList))) {
            allErrors.add(error);
          }
        }
      }
    }

    final ArrayList fixes = new ArrayList();
    for (Pair error : allErrors) {
      fixes.add(new ParameterCastFix(error.first, error.second, args.get(error.first)));
    }
    return fixes.toArray(new LocalQuickFix[fixes.size()]);
  }

  @NotNull
  public static String buildArgTypesList(@NotNull PsiType[] argTypes) {
    StringBuilder builder = new StringBuilder();
    builder.append("(");
    for (int i = 0; i < argTypes.length; i++) {
      if (i > 0) {
        builder.append(", ");
      }
      PsiType argType = argTypes[i];
      builder.append(argType != null ? argType.getInternalCanonicalText() : "?");
    }
    builder.append(")");
    return builder.toString();
  }

  public static boolean checkCategoryQualifier(@NotNull GrReferenceExpression place,
                                               @Nullable GrExpression qualifier,
                                               @NotNull PsiMethod gdkMethod,
                                               @Nullable PsiSubstitutor substitutor) {
    PsiClass categoryAnnotationOwner = inferCategoryAnnotationOwner(place, qualifier);

    if (categoryAnnotationOwner != null) {
      PsiClassType categoryType = GdkMethodUtil.getCategoryType(categoryAnnotationOwner);
      if (categoryType != null) {
        return GdkMethodUtil.isCategoryMethod(gdkMethod, categoryType, qualifier, substitutor);
      }
    }

    return false;
  }

  @Nullable
  public static PsiClass inferCategoryAnnotationOwner(@NotNull GrReferenceExpression place, @Nullable GrExpression qualifier) {
    if (qualifier == null) {
      GrMethod container = PsiTreeUtil.getParentOfType(place, GrMethod.class, true, GrMember.class);
      if (container != null &&
          !container.hasModifierProperty(PsiModifier.STATIC)) { //only instance methods can be qualified by category class
        return container.getContainingClass();
      }
    }
    else if (PsiUtil.isThisReference(qualifier)) {
      PsiElement resolved = ((GrReferenceExpression)qualifier).resolve();
      if (resolved instanceof PsiClass) {
        return (PsiClass)resolved;
      }
    }
    return null;
  }

  @Nullable
  public static String getLValueVarName(@NotNull PsiElement highlight) {
    final PsiElement parent = highlight.getParent();
    if (parent instanceof GrVariable) {
      return ((GrVariable)parent).getName();
    }
    else if (highlight instanceof GrReferenceExpression &&
             parent instanceof GrAssignmentExpression &&
             ((GrAssignmentExpression)parent).getLValue() == highlight) {
      final PsiElement resolved = ((GrReferenceExpression)highlight).resolve();
      if (resolved instanceof GrVariable && PsiUtil.isLocalVariable(resolved)) {
        return ((GrVariable)resolved).getName();
      }
    }

    return null;
  }

  @Nullable
  public static List getExpressionArgumentsOfCall(@NotNull GrArgumentList argumentList) {
    final ArrayList args = ContainerUtil.newArrayList();

    for (GroovyPsiElement arg : argumentList.getAllArguments()) {
      if (arg instanceof GrSpreadArgument) {
        GrExpression spreaded = ((GrSpreadArgument)arg).getArgument();
        if (spreaded instanceof GrListOrMap && !((GrListOrMap)spreaded).isMap()) {
          Collections.addAll(args, ((GrListOrMap)spreaded).getInitializers());
        }
        else {
          return null;
        }
      }
      else if (arg instanceof GrExpression) {
        args.add((GrExpression)arg);
      }
      else if (arg instanceof GrNamedArgument) {
        args.add(((GrNamedArgument)arg).getExpression());
      }
    }

    final PsiElement parent = argumentList.getParent();
    if (parent instanceof GrIndexProperty && PsiUtil.isLValue((GroovyPsiElement)parent)) {
      args.add(TypeInferenceHelper.getInitializerFor((GrExpression)parent));
    }
    else if (parent instanceof GrMethodCallExpression) {
      ContainerUtil.addAll(args, ((GrMethodCallExpression)parent).getClosureArguments());
    }
    return args;
  }

  static boolean isImplicitReturnStatement(@NotNull GrExpression expression) {
    GrControlFlowOwner flowOwner = ControlFlowUtils.findControlFlowOwner(expression);
    return flowOwner != null &&
        PsiUtil.isExpressionStatement(expression) &&
        ControlFlowUtils.isReturnValue(expression, flowOwner) &&
        !PsiUtil.isVoidMethodCall(expression);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy