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

org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.path.DefaultCallExpressionTypeCalculator 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.lang.psi.impl.statements.expressions.path;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
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.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
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.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrLiteralClassType;
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.typeEnhancers.GrCallExpressionTypeCalculator;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;

import java.util.Set;

import static org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isCompileStatic;

/**
 * @author sergey.evdokimov
 */
public class DefaultCallExpressionTypeCalculator extends GrCallExpressionTypeCalculator {
  private static final Logger LOG = Logger.getInstance(DefaultCallExpressionTypeCalculator.class);

  @Override
  public PsiType calculateReturnType(@NotNull GrMethodCall callExpression, GroovyResolveResult[] resolveResults) {
    GrExpression invoked = callExpression.getInvokedExpression();
    if (invoked instanceof GrReferenceExpression) {
      GrReferenceExpression refExpr = (GrReferenceExpression) invoked;
      PsiManager manager = callExpression.getManager();
      PsiType result = null;
      for (GroovyResolveResult resolveResult : resolveResults) {
        PsiType returnType = calculateReturnTypeInner(callExpression, refExpr, resolveResult);
        if (returnType == null) return null;

        PsiType nonVoid = PsiType.VOID.equals(returnType) && !isCompileStatic(callExpression) ? PsiType.NULL : returnType;

        PsiType normalized = nonVoid instanceof GrLiteralClassType
                             ? nonVoid
                             : TypesUtil.substituteAndNormalizeType(nonVoid, resolveResult.getSubstitutor(), resolveResult.getSpreadState(),
                                                                    callExpression);

        LOG.assertTrue(normalized != null, "return type: " + returnType + "; substitutor: " + resolveResult.getSubstitutor());

        if (result == null || normalized.isAssignableFrom(result)) {
          result = normalized;
        }
        else if (!result.isAssignableFrom(normalized)) {
          result = TypesUtil.getLeastUpperBound(result, normalized, manager);
        }
      }

      return result;
    }
    else {
      return extractReturnTypeFromType(invoked.getType(), false, callExpression);
    }
  }

  @Nullable
  private static PsiType calculateReturnTypeInner(GrMethodCall callExpression,
                                                  GrReferenceExpression refExpr,
                                                  GroovyResolveResult resolveResult) {
    PsiElement resolved = resolveResult.getElement();
    if (resolved instanceof PsiMethod) {
      PsiMethod method = (PsiMethod)resolved;
      if (resolveResult.isInvokedOnProperty()) {
        final PsiType propertyType = PsiUtil.getSmartReturnType(method);
        return extractReturnTypeFromType(propertyType, true, callExpression);
      }
      else {
        PsiType closureReturnType = getClosureMethodsReturnType(callExpression, refExpr, method);
        if (closureReturnType != null) {
          return closureReturnType;
        }
        else {
          final PsiType smartReturnType = PsiUtil.getSmartReturnType(method);
          return smartReturnType;
        }
      }
    }
    else if (resolved instanceof GrVariable) {
      PsiType refType = refExpr.getType();
      refType = TypesUtil.boxPrimitiveType(refType, callExpression.getManager(), callExpression.getResolveScope());
      final PsiType type = refType == null ? ((GrVariable)resolved).getTypeGroovy() : refType;
      return extractReturnTypeFromType(type, false, callExpression);
    }
    return null;
  }

  @Nullable
  private static PsiType extractReturnTypeFromType(PsiType type, boolean returnTypeIfFail, GrMethodCall callExpression) {
    PsiType returnType = returnTypeIfFail ? type: null;
    if (type instanceof GrClosureType) {
      returnType = GrClosureSignatureUtil.getReturnType(((GrClosureType)type).getSignature(), callExpression);
    }
    else if (TypesUtil.isPsiClassTypeToClosure(type)) {
      assert type instanceof PsiClassType;
      final PsiType[] parameters = ((PsiClassType)type).getParameters();
      if (parameters.length == 1) {
        returnType = parameters[0];
      }
    }
    else if (type instanceof PsiClassType) {
      final GrExpression invoked = callExpression.getInvokedExpression();
      final GroovyResolveResult[] calls = ResolveUtil
        .getMethodCandidates(type, "call", invoked != null ? invoked : callExpression, PsiUtil.getArgumentTypes(invoked, false));
      returnType = null;
      final PsiManager manager = callExpression.getManager();
      for (GroovyResolveResult call : calls) {
        final PsiType substituted = ResolveUtil.extractReturnTypeFromCandidate(call, callExpression, PsiUtil.getArgumentTypes(invoked, true));
        returnType = TypesUtil.getLeastUpperBoundNullable(returnType, substituted, manager);
      }
    }
    return returnType;
  }


  private static final Set CLOSURE_METHODS = new HashSet();
  static {
    CLOSURE_METHODS.add("call");
    CLOSURE_METHODS.add("curry");
    CLOSURE_METHODS.add("ncurry");
    CLOSURE_METHODS.add("rcurry");
    CLOSURE_METHODS.add("memoize");
    CLOSURE_METHODS.add("trampoline");
  }
  @Nullable
  private static PsiType getClosureMethodsReturnType(GrMethodCall callExpression, GrReferenceExpression refExpr, PsiMethod resolved) {
    PsiClass clazz = resolved.getContainingClass();
    if (clazz == null || !GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(clazz.getQualifiedName())) return null;

    if (!CLOSURE_METHODS.contains(resolved.getName())) return null;

    GrExpression qualifier = refExpr.getQualifierExpression();
    if (qualifier == null) return null;

    PsiType qType = qualifier.getType();
    if (!(qType instanceof GrClosureType)) return null;

    if ("call".equals(resolved.getName())) {
      return GrClosureSignatureUtil.getReturnType(((GrClosureType)qType).getSignature(), callExpression);
    }
    else if ("curry".equals(resolved.getName()) || "trampoline".equals(resolved.getName())) {
      return ((GrClosureType)qType).curry(PsiUtil.getArgumentTypes(refExpr, false), 0, callExpression);
    }
    else if ("memoize".equals(resolved.getName())) {
      return qType;
    }
    else if ("rcurry".equals(resolved.getName())) {
      return ((GrClosureType)qType).curry(PsiUtil.getArgumentTypes(refExpr, false), -1, callExpression);
    }
    else if ("ncurry".equals(resolved.getName())) {
      final GrArgumentList argList = callExpression.getArgumentList();
      if (argList != null) {
        final GrExpression[] arguments = argList.getExpressionArguments();
        if (arguments.length > 0) {
          final GrExpression first = arguments[0];
          if (first instanceof GrLiteral) {
            final Object value = ((GrLiteral)first).getValue();
            if (value instanceof Integer) {
              final PsiType[] argTypes = PsiUtil.getArgumentTypes(refExpr, false);
              if (argTypes != null) {
                return ((GrClosureType)qType).curry(ArrayUtil.remove(argTypes, 0), (Integer)value, callExpression);
              }
            }
          }
        }
      }
      return qType;
    }
    return null;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy