/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.lang.psi.impl.source.resolve.methodResolving; import java.util.ArrayList; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Trinity; import com.intellij.psi.PsiElement; import com.intellij.util.SmartList; import consulo.annotations.RequiredReadAction; import consulo.csharp.lang.psi.CSharpCallArgument; import consulo.csharp.lang.psi.CSharpCallArgumentListOwner; import consulo.csharp.lang.psi.CSharpNamedCallArgument; import consulo.csharp.lang.psi.CSharpSimpleParameterInfo; import consulo.csharp.lang.psi.impl.CSharpTypeUtil; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NCallArgument; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NEmptyParamsCallArgument; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NErrorCallArgument; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NNamedCallArgument; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NParamsCallArgument; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.context.MethodParameterResolveContext; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.context.ParameterResolveContext; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.context.SimpleParameterResolveContext; import consulo.dotnet.psi.DotNetExpression; import consulo.dotnet.psi.DotNetParameter; import consulo.dotnet.psi.DotNetParameterListOwner; import consulo.dotnet.resolve.DotNetTypeRef; import consulo.dotnet.util.ArrayUtil2; /** * @author VISTALL * @since 02.11.14 */ public class MethodResolver { @NotNull private static List<NCallArgument> buildCallArguments(@NotNull DotNetTypeRef[] callArgumentTypeRefs, @NotNull DotNetTypeRef[] parameterTypeRefs) { List<NCallArgument> list = new ArrayList<>(callArgumentTypeRefs.length); for(int i = 0; i < callArgumentTypeRefs.length; i++) { DotNetTypeRef callArgumentTypeRef = callArgumentTypeRefs[i]; DotNetTypeRef dotNetTypeRef = ArrayUtil2.safeGet(parameterTypeRefs, i); list.add(new NCallArgument(callArgumentTypeRef, null, dotNetTypeRef)); } return list; } @NotNull @RequiredReadAction public static List<NCallArgument> buildCallArguments(@NotNull CSharpCallArgument[] callArguments, @NotNull CSharpSimpleParameterInfo[] parameterInfos, @NotNull PsiElement scope) { return buildCallArguments(callArguments, scope, new SimpleParameterResolveContext(parameterInfos)); } @NotNull @RequiredReadAction public static List<NCallArgument> buildCallArguments(@NotNull CSharpCallArgument[] callArguments, @NotNull DotNetParameterListOwner parameterListOwner, @NotNull PsiElement scope, boolean resolveFromParent) { return buildCallArguments(callArguments, scope, new MethodParameterResolveContext(parameterListOwner, scope, resolveFromParent)); } @NotNull @RequiredReadAction public static List<NCallArgument> buildCallArguments(@NotNull CSharpCallArgument[] callArguments, @NotNull DotNetParameterListOwner parameterListOwner, @NotNull PsiElement scope) { return buildCallArguments(callArguments, parameterListOwner, scope, false); } @NotNull @RequiredReadAction private static <T> List<NCallArgument> buildCallArguments(@NotNull CSharpCallArgument[] callArguments, @NotNull PsiElement scope, @NotNull ParameterResolveContext<T> context) { List<NCallArgument> list = new ArrayList<>(context.getParametersSize()); List<CSharpCallArgument> paramsArguments = new SmartList<>(); int i = 0; for(CSharpCallArgument argument : callArguments) { DotNetTypeRef expressionTypeRef = DotNetTypeRef.ERROR_TYPE; String name; DotNetExpression argumentExpression = argument.getArgumentExpression(); if(argumentExpression != null) { expressionTypeRef = argumentExpression.toTypeRef(context.isResolveFromParentTypeRef()); } if(argument instanceof CSharpNamedCallArgument) { name = ((CSharpNamedCallArgument) argument).getName(); list.add(new NNamedCallArgument(expressionTypeRef, argument, context.getParameterByName(name), name)); i++; } else { T parameter = context.getParameterByIndex(i++); if(parameter == null) { DotNetParameter paramsParameter = context.getParamsParameter(); if(paramsParameter != null) { // we already have expression for params parameter NCallArgument paramsValue = findByName(list, paramsParameter.getName()); if(paramsValue != null) { list.add(new NCallArgument(expressionTypeRef, argument, null)); } else { // if expression is like inner params type if(CSharpTypeUtil.isInheritableWithImplicit(context.getInnerParamsParameterTypeRef(), expressionTypeRef, scope)) { // store to params list paramsArguments.add(argument); } // if params type equal expression parameter pull it as argument of parameter else if(CSharpTypeUtil.isInheritableWithImplicit(context.getParamsParameterTypeRef(), expressionTypeRef, scope)) { list.add(new NCallArgument(expressionTypeRef, argument, paramsParameter)); } else { list.add(new NCallArgument(expressionTypeRef, argument, null)); } } } else { list.add(new NCallArgument(expressionTypeRef, argument, null)); } } else { DotNetParameter paramsParameter = context.getParamsParameter(); if(paramsParameter == parameter) { NCallArgument paramsValue = findByName(list, paramsParameter.getName()); if(paramsValue != null) { list.add(new NCallArgument(expressionTypeRef, argument, null)); } else { // if expression is like inner params type if(CSharpTypeUtil.isInheritableWithImplicit(context.getInnerParamsParameterTypeRef(), expressionTypeRef, scope)) { // store to params list paramsArguments.add(argument); } // if params type equal expression parameter pull it as argument of parameter else if(CSharpTypeUtil.isInheritableWithImplicit(context.getParamsParameterTypeRef(), expressionTypeRef, scope)) { list.add(new NCallArgument(expressionTypeRef, argument, paramsParameter)); } else { list.add(new NCallArgument(expressionTypeRef, argument, null)); } } } else { list.add(new NCallArgument(expressionTypeRef, argument, parameter)); } } } } // if we have params arguments add to list it if(!paramsArguments.isEmpty()) { list.add(new NParamsCallArgument(paramsArguments, context.getParamsParameter())); } else { // if we have params parameter DotNetParameter paramsParameter = context.getParamsParameter(); if(paramsParameter != null) { // but - no arguments for it, add empty argument NCallArgument nCallArgument = findByName(list, paramsParameter.getName()); if(nCallArgument == null) { list.add(new NEmptyParamsCallArgument(paramsParameter)); } } } for(T parameter : context.getParameters()) { NCallArgument nCallArgument = findByParameterObject(list, parameter); if(nCallArgument != null) { continue; } final Trinity<String, DotNetTypeRef, Boolean> parameterInfo = context.getParameterInfo(parameter); if(parameterInfo.getThird()) { list.add(new NNamedCallArgument(parameterInfo.getSecond(), null, parameter, parameterInfo.getFirst())); } else { list.add(new NErrorCallArgument(parameter)); } } return list; } @Nullable private static NCallArgument findByName(List<NCallArgument> arguments, @Nullable String name) { if(name == null) { return null; } for(NCallArgument argument : arguments) { if(argument instanceof NNamedCallArgument && Comparing.equal(((NNamedCallArgument) argument).getName(), name)) { return argument; } String parameterName = argument.getParameterName(); if(parameterName != null && Comparing.equal(parameterName, name)) { return argument; } } return null; } @Nullable private static NCallArgument findByParameterObject(List<NCallArgument> arguments, Object parameterObject) { for(NCallArgument argument : arguments) { if(argument.getParameterObject() == parameterObject) { return argument; } } return null; } @NotNull @RequiredReadAction public static MethodCalcResult calc(@NotNull CSharpCallArgument[] callArguments, @NotNull DotNetParameterListOwner parameterListOwner, @NotNull PsiElement scope, boolean resolveFromParent) { List<NCallArgument> list = buildCallArguments(callArguments, parameterListOwner, scope, resolveFromParent); return calc(list, scope); } @NotNull @RequiredReadAction public static MethodCalcResult calc(@NotNull CSharpCallArgument[] callArguments, @NotNull DotNetParameterListOwner parameterListOwner, @NotNull PsiElement scope) { return calc(callArguments, parameterListOwner, scope, false); } @NotNull @RequiredReadAction public static MethodCalcResult calc(@NotNull CSharpCallArgumentListOwner callArgumentListOwner, @NotNull CSharpSimpleParameterInfo[] p, @NotNull PsiElement scope) { List<NCallArgument> list = buildCallArguments(callArgumentListOwner.getCallArguments(), scope, new SimpleParameterResolveContext(p)); return calc(list, scope); } @NotNull @RequiredReadAction public static MethodCalcResult calc(@NotNull DotNetTypeRef[] expressionTypeRefs, @NotNull DotNetTypeRef[] parameterTypeRefs, @NotNull PsiElement scope) { List<NCallArgument> list = buildCallArguments(expressionTypeRefs, parameterTypeRefs); return calc(list, scope); } @NotNull @RequiredReadAction public static MethodCalcResult calc(@NotNull CSharpCallArgumentListOwner callArgumentListOwner, @NotNull DotNetParameterListOwner parameterListOwner, @NotNull PsiElement scope) { List<NCallArgument> list = buildCallArguments(callArgumentListOwner.getCallArguments(), parameterListOwner, scope); return calc(list, scope); } @NotNull @RequiredReadAction public static MethodCalcResult calc(@NotNull List<NCallArgument> arguments, @NotNull PsiElement scope) { int weight = 0; boolean valid = true; for(NCallArgument argument : arguments) { switch(argument.calcValid(scope)) { case NCallArgument.EQUAL: weight -= 50000; break; case NCallArgument.PARAMS: weight -= 100000; break; case NCallArgument.INSTANCE_OF: weight -= 1000000; break; case NCallArgument.PARAMS_INSTANCE_OF: weight -= 2000000; break; default: valid = false; break; } } return new MethodCalcResult(valid, weight, arguments); } }