Added 'getParameterForArgument' util function (using resolved call)

Removed similar util methods (using psi):
getParameterCorrespondingToValueArgumentPassedInCall
getParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList
This commit is contained in:
Svetlana Isakova
2014-07-16 12:03:16 +04:00
parent 2ae87cae4a
commit b3ad725e8d
5 changed files with 77 additions and 129 deletions
@@ -26,7 +26,6 @@ import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMapping;
import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMatch;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
import org.jetbrains.jet.lang.types.lang.InlineUtil;
public class InlineDescriptorUtils {
@@ -54,7 +53,8 @@ public class InlineDescriptorUtils {
CallableDescriptor resultingDescriptor = resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
if (resultingDescriptor instanceof SimpleFunctionDescriptor &&
((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline()) {
ValueArgument argument = getContainingArgument(containingFunction, call);
ValueArgument argument = CallUtilPackage.getValueArgumentForExpression(
resolvedCall.getCall(), (JetFunctionLiteralExpression) containingFunction);
if (argument != null) {
ArgumentMapping mapping = resolvedCall.getArgumentMapping(argument);
if (mapping instanceof ArgumentMatch) {
@@ -77,22 +77,6 @@ public class InlineDescriptorUtils {
return fromFunction == containingFunctionDescriptor;
}
@Nullable
public static ValueArgument getContainingArgument(PsiElement expression, @NotNull JetExpression stopAtCall) {
if (expression instanceof JetValueArgument) {
return (JetValueArgument) expression;
}
while (expression != null && expression.getParent() != stopAtCall) {
PsiElement parent = expression.getParent();
if (parent instanceof JetValueArgument) {
return (JetValueArgument) parent;
}
expression = parent;
}
return expression != null ? CallMaker.makeValueArgument((JetExpression) expression) : null;
}
@Nullable
public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
DeclarationDescriptor currentDescriptor = strict ? descriptor.getContainingDeclaration() : descriptor;
@@ -24,7 +24,6 @@ import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMatch
import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMatchStatus
import org.jetbrains.jet.lang.psi.Call
import org.jetbrains.jet.lang.psi.ValueArgument
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.calls.ArgumentTypeResolver
import org.jetbrains.jet.lang.psi.JetElement
@@ -48,6 +47,8 @@ import org.jetbrains.jet.lang.psi.psiUtil.getTextWithLocation
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.lang.psi.JetFunctionLiteralArgument
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
// resolved call
@@ -74,6 +75,10 @@ public fun <D : CallableDescriptor> ResolvedCall<D>.hasTypeMismatchErrorOnParame
}
}
public fun <D : CallableDescriptor> ResolvedCall<D>.getParameterForArgument(valueArgument: ValueArgument?): ValueParameterDescriptor? {
return (valueArgument?.let { getArgumentMapping(it) } as? ArgumentMatch)?.valueParameter
}
// call
public fun Call.hasUnresolvedArguments(context: BindingContext): Boolean {
@@ -93,6 +98,20 @@ public fun JetCallExpression.getValueArgumentsInParentheses(): List<ValueArgumen
[suppress("UNCHECKED_CAST")]
private fun List<ValueArgument?>.filterArgsInParentheses() = filter { it !is JetFunctionLiteralArgument } as List<ValueArgument>
public fun Call.getValueArgumentForExpression(expression: JetExpression): ValueArgument? {
fun JetElement.deparenthesizeStructurally(): JetElement? {
val deparenthesized = if (this is JetExpression) JetPsiUtil.deparenthesizeOnce(this, false) else this
return when {
deparenthesized != this -> deparenthesized
this is JetFunctionLiteralExpression -> this.getFunctionLiteral()
this is JetFunctionLiteral -> this.getBodyExpression()
else -> null
}
}
fun JetElement.isParenthesizedExpression() = stream(this) { it.deparenthesizeStructurally() }.any { it == expression }
return getValueArguments().firstOrNull { it?.getArgumentExpression()?.isParenthesizedExpression() ?: false }
}
// Get call / resolved call from binding context
public fun JetElement.getCalleeExpressionIfAny(): JetExpression? {
@@ -24,10 +24,13 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeProjection;
import org.jetbrains.jet.lang.types.TypeUtils;
@@ -47,13 +50,13 @@ public class ChangeFunctionLiteralReturnTypeFix extends JetIntentionAction<JetFu
private final JetTypeReference functionLiteralReturnTypeRef;
private IntentionAction appropriateQuickFix = null;
public ChangeFunctionLiteralReturnTypeFix(@NotNull JetFunctionLiteralExpression element, @NotNull JetType type) {
super(element);
public ChangeFunctionLiteralReturnTypeFix(@NotNull JetFunctionLiteralExpression functionLiteralExpression, @NotNull JetType type) {
super(functionLiteralExpression);
renderedType = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(type);
functionLiteralReturnTypeRef = element.getFunctionLiteral().getReturnTypeRef();
functionLiteralReturnTypeRef = functionLiteralExpression.getFunctionLiteral().getReturnTypeRef();
BindingContext context = ResolvePackage.getBindingContext(element.getContainingJetFile());
JetType functionLiteralType = context.get(BindingContext.EXPRESSION_TYPE, element);
BindingContext context = ResolvePackage.getBindingContext(functionLiteralExpression.getContainingJetFile());
JetType functionLiteralType = context.get(BindingContext.EXPRESSION_TYPE, functionLiteralExpression);
assert functionLiteralType != null : "Type of function literal not available in binding context";
ClassDescriptor functionClass = KotlinBuiltIns.getInstance().getFunction(functionLiteralType.getArguments().size() - 1);
@@ -66,8 +69,8 @@ public class ChangeFunctionLiteralReturnTypeFix extends JetIntentionAction<JetFu
functionClassTypeParameters.add(type);
JetType eventualFunctionLiteralType = TypeUtils.substituteParameters(functionClass, functionClassTypeParameters);
JetProperty correspondingProperty = PsiTreeUtil.getParentOfType(element, JetProperty.class);
if (correspondingProperty != null && QuickFixUtil.canEvaluateTo(correspondingProperty.getInitializer(), element)) {
JetProperty correspondingProperty = PsiTreeUtil.getParentOfType(functionLiteralExpression, JetProperty.class);
if (correspondingProperty != null && QuickFixUtil.canEvaluateTo(correspondingProperty.getInitializer(), functionLiteralExpression)) {
JetTypeReference correspondingPropertyTypeRef = correspondingProperty.getTypeRef();
JetType propertyType = context.get(BindingContext.TYPE, correspondingPropertyTypeRef);
if (propertyType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, propertyType)) {
@@ -76,18 +79,24 @@ public class ChangeFunctionLiteralReturnTypeFix extends JetIntentionAction<JetFu
return;
}
JetParameter correspondingParameter = QuickFixUtil.getParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(element);
if (correspondingParameter != null) {
JetTypeReference correspondingParameterTypeRef = correspondingParameter.getTypeReference();
JetType parameterType = context.get(BindingContext.TYPE, correspondingParameterTypeRef);
if (parameterType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parameterType)) {
appropriateQuickFix = new ChangeParameterTypeFix(correspondingParameter, eventualFunctionLiteralType);
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getParentResolvedCall(
functionLiteralExpression, context, true);
if (resolvedCall != null) {
ValueArgument valueArgument = CallUtilPackage.getValueArgumentForExpression(resolvedCall.getCall(), functionLiteralExpression);
JetParameter correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument);
if (correspondingParameter != null) {
JetTypeReference correspondingParameterTypeRef = correspondingParameter.getTypeReference();
JetType parameterType = context.get(BindingContext.TYPE, correspondingParameterTypeRef);
if (parameterType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parameterType)) {
appropriateQuickFix = new ChangeParameterTypeFix(correspondingParameter, eventualFunctionLiteralType);
}
return;
}
return;
}
JetFunction parentFunction = PsiTreeUtil.getParentOfType(element, JetFunction.class, true);
if (parentFunction != null && QuickFixUtil.canFunctionOrGetterReturnExpression(parentFunction, element)) {
JetFunction parentFunction = PsiTreeUtil.getParentOfType(functionLiteralExpression, JetFunction.class, true);
if (parentFunction != null && QuickFixUtil.canFunctionOrGetterReturnExpression(parentFunction, functionLiteralExpression)) {
JetTypeReference parentFunctionReturnTypeRef = parentFunction.getReturnTypeRef();
JetType parentFunctionReturnType = context.get(BindingContext.TYPE, parentFunctionReturnTypeRef);
if (parentFunctionReturnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parentFunctionReturnType)) {
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters2;
import org.jetbrains.jet.lang.diagnostics.Errors;
@@ -103,24 +104,15 @@ public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsF
}
}
// Change type of a function parameter in case TYPE_MISMATCH is reported on expression passed as value argument of call.
// 1) When an argument is a dangling function literal:
JetFunctionLiteralArgument functionLiteralArgument =
QuickFixUtil.getParentElementOfType(diagnostic, JetFunctionLiteralArgument.class);
JetFunctionLiteralExpression functionLiteralExpression = functionLiteralArgument != null ? functionLiteralArgument.getFunctionLiteral() : null;
if (functionLiteralExpression != null && functionLiteralExpression.getBodyExpression() == expression) {
JetParameter correspondingParameter =
QuickFixUtil.getParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(functionLiteralExpression);
JetType functionLiteralExpressionType = context.get(BindingContext.EXPRESSION_TYPE, functionLiteralExpression);
if (correspondingParameter != null && functionLiteralExpressionType != null) {
actions.add(new ChangeParameterTypeFix(correspondingParameter, functionLiteralExpressionType));
}
}
// 2) When an argument is passed inside value argument list:
else {
JetValueArgument valueArgument = QuickFixUtil.getParentElementOfType(diagnostic, JetValueArgument.class);
if (valueArgument != null && QuickFixUtil.canEvaluateTo(valueArgument.getArgumentExpression(), expression)) {
JetParameter correspondingParameter = QuickFixUtil.getParameterCorrespondingToValueArgumentPassedInCall(valueArgument);
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getParentResolvedCall(expression, context, true);
if (resolvedCall != null) {
// to fix 'type mismatch' on 'if' branches
// todo: the same with 'when'
JetExpression parentIf = QuickFixUtil.getParentIfForBranch(expression);
JetExpression argumentExpression = (parentIf != null) ? parentIf : expression;
ValueArgument valueArgument = CallUtilPackage.getValueArgumentForExpression(resolvedCall.getCall(), argumentExpression);
if (valueArgument != null) {
JetParameter correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument);
JetType valueArgumentType = context.get(BindingContext.EXPRESSION_TYPE, valueArgument.getArgumentExpression());
if (correspondingParameter != null && valueArgumentType != null) {
actions.add(new ChangeParameterTypeFix(correspondingParameter, valueArgumentType));
@@ -132,7 +124,7 @@ public class QuickFixFactoryForTypeMismatchError implements JetIntentionActionsF
@Nullable
private static JetFunction getFunctionDeclaration(@NotNull ResolvedCall<?> resolvedCall) {
PsiElement result = QuickFixUtil.safeGetDeclaration(resolvedCall);
PsiElement result = QuickFixUtil.safeGetDeclaration(resolvedCall.getResultingDescriptor());
if (result instanceof JetFunction) {
return (JetFunction) result;
}
@@ -23,7 +23,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.sun.codemodel.internal.JVar;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
@@ -45,7 +44,6 @@ import org.jetbrains.jet.plugin.references.BuiltInsReferenceResolver;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import java.util.Set;
public class QuickFixUtil {
@@ -107,77 +105,16 @@ public class QuickFixUtil {
}
@Nullable
public static JetParameterList getParameterListOfCallee(@NotNull JetCallExpression callExpression) {
BindingContext context = ResolvePackage.getBindingContext(callExpression.getContainingJetFile());
ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(callExpression, context);
if (resolvedCall == null) return null;
PsiElement declaration = safeGetDeclaration(resolvedCall);
if (declaration instanceof JetFunction) {
return ((JetFunction) declaration).getValueParameterList();
}
if (declaration instanceof JetClass) {
return ((JetClass) declaration).getPrimaryConstructorParameterList();
}
return null;
}
@Nullable
public static PsiElement safeGetDeclaration(@NotNull ResolvedCall<?> resolvedCall) {
List<PsiElement> declarations = DescriptorToSourceUtils.descriptorToDeclarations(resolvedCall.getResultingDescriptor());
public static PsiElement safeGetDeclaration(@Nullable CallableDescriptor descriptor) {
//do not create fix if descriptor has more than one overridden declaration
if (declarations.size() == 1) {
return declarations.iterator().next();
}
return null;
}
//todo remove, use value argument to parameter map
@Nullable
public static JetParameter getParameterCorrespondingToFunctionLiteralPassedOutsideArgumentList(@NotNull JetFunctionLiteralExpression functionLiteralExpression) {
JetFunctionLiteralArgument functionLiteralArgument = PsiTreeUtil.getParentOfType(functionLiteralExpression, JetFunctionLiteralArgument.class);
if (functionLiteralArgument == null || functionLiteralArgument.getFunctionLiteral() != functionLiteralExpression) return null;
if (!(functionLiteralArgument.getParent() instanceof JetCallExpression)) {
return null;
}
JetCallExpression callExpression = (JetCallExpression) functionLiteralArgument.getParent();
JetParameterList parameterList = getParameterListOfCallee(callExpression);
if (parameterList == null) return null;
return parameterList.getParameters().get(parameterList.getParameters().size() - 1);
if (descriptor == null || descriptor.getOverriddenDescriptors().size() > 1) return null;
return DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
}
@Nullable
public static JetParameter getParameterCorrespondingToValueArgumentPassedInCall(@NotNull JetValueArgument valueArgument) {
if (!(valueArgument.getParent() instanceof JetValueArgumentList)) {
return null;
}
JetValueArgumentList valueArgumentList = (JetValueArgumentList) valueArgument.getParent();
if (!(valueArgumentList.getParent() instanceof JetCallExpression)) {
return null;
}
JetCallExpression callExpression = (JetCallExpression) valueArgumentList.getParent();
JetParameterList parameterList = getParameterListOfCallee(callExpression);
if (parameterList == null) return null;
List<JetParameter> parameters = parameterList.getParameters();
int position = valueArgumentList.getArguments().indexOf(valueArgument);
if (position == -1) return null;
if (valueArgument.isNamed()) {
JetValueArgumentName valueArgumentName = valueArgument.getArgumentName();
JetSimpleNameExpression referenceExpression = valueArgumentName == null ? null : valueArgumentName.getReferenceExpression();
String valueArgumentNameAsString = referenceExpression == null ? null : referenceExpression.getReferencedName();
if (valueArgumentNameAsString == null) return null;
for (JetParameter parameter: parameters) {
if (valueArgumentNameAsString.equals(parameter.getName())) {
return parameter;
}
}
return null;
}
else {
if (position >= parameters.size()) return null;
return parameters.get(position);
}
public static JetParameter getParameterDeclarationForValueArgument(@NotNull ResolvedCall<?> resolvedCall, @Nullable ValueArgument valueArgument) {
ValueParameterDescriptor parameterDescriptor = CallUtilPackage.getParameterForArgument(resolvedCall, valueArgument);
return (JetParameter) safeGetDeclaration(parameterDescriptor);
}
private static boolean equalOrLastInThenOrElse(JetExpression thenOrElse, JetExpression expression) {
@@ -186,6 +123,17 @@ public class QuickFixUtil {
PsiTreeUtil.getNextSiblingOfType(expression, JetExpression.class) == null;
}
@Nullable
public static JetIfExpression getParentIfForBranch(@Nullable JetExpression expression) {
JetIfExpression ifExpression = PsiTreeUtil.getParentOfType(expression, JetIfExpression.class, true);
if (ifExpression == null) return null;
if (equalOrLastInThenOrElse(ifExpression.getThen(), expression)
|| equalOrLastInThenOrElse(ifExpression.getElse(), expression)) {
return ifExpression;
}
return null;
}
public static boolean canEvaluateTo(JetExpression parent, JetExpression child) {
if (parent == null || child == null) {
return false;
@@ -195,12 +143,8 @@ public class QuickFixUtil {
child = (JetExpression) child.getParent();
continue;
}
JetIfExpression jetIfExpression = PsiTreeUtil.getParentOfType(child, JetIfExpression.class, true);
if (jetIfExpression == null) return false;
if (!equalOrLastInThenOrElse(jetIfExpression.getThen(), child) && !equalOrLastInThenOrElse(jetIfExpression.getElse(), child)) {
return false;
}
child = jetIfExpression;
child = getParentIfForBranch(child);
if (child == null) return false;
}
return true;
}