/* * 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; import java.util.ArrayList; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import consulo.annotations.RequiredReadAction; import consulo.csharp.lang.psi.CSharpElementVisitor; import consulo.csharp.lang.psi.CSharpLambdaParameter; import consulo.csharp.lang.psi.CSharpLambdaParameterList; import consulo.csharp.lang.psi.CSharpRecursiveElementVisitor; import consulo.csharp.lang.psi.CSharpSimpleParameterInfo; import consulo.csharp.lang.psi.CSharpTokens; import consulo.csharp.lang.psi.impl.CSharpImplicitReturnModel; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpGenericWrapperTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpLambdaResolveResult; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpLambdaTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpTypeRefByQName; import consulo.dotnet.DotNetTypes; import consulo.dotnet.psi.DotNetExpression; import consulo.dotnet.psi.DotNetModifier; import consulo.dotnet.psi.DotNetModifierList; import consulo.dotnet.psi.DotNetStatement; import consulo.dotnet.resolve.DotNetTypeRef; import consulo.dotnet.resolve.DotNetTypeRefUtil; /** * @author VISTALL * @since 04.01.14. */ public class CSharpLambdaExpressionImpl extends CSharpExpressionImpl implements CSharpAnonymousMethodExpression { public CSharpLambdaExpressionImpl(@NotNull ASTNode node) { super(node); } @RequiredReadAction @Override public boolean hasModifier(@NotNull DotNetModifier modifier) { return getModifierList().hasModifier(modifier); } @RequiredReadAction @NotNull @Override public DotNetModifierList getModifierList() { return CachedValuesManager.getManager(getProject()).createCachedValue(() -> CachedValueProvider.Result.<DotNetModifierList>create(new CSharpAnonymousModifierListImpl (CSharpLambdaExpressionImpl.this), CSharpLambdaExpressionImpl.this), false).getValue(); } @Nullable @Override public PsiElement getCodeBlock() { DotNetExpression singleExpression = findChildByClass(DotNetExpression.class); if(singleExpression != null) { return singleExpression; } return findChildByClass(DotNetStatement.class); } @NotNull @RequiredReadAction public PsiElement getDArrow() { return findNotNullChildByType(CSharpTokens.DARROW); } @Override public void accept(@NotNull CSharpElementVisitor visitor) { visitor.visitLambdaExpression(this); } @Nullable public CSharpLambdaParameterList getParameterList() { return findChildByClass(CSharpLambdaParameterList.class); } @NotNull public CSharpLambdaParameter[] getParameters() { CSharpLambdaParameterList parameterList = getParameterList(); return parameterList == null ? CSharpLambdaParameterImpl.EMPTY_ARRAY : parameterList.getParameters(); } @Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { for(CSharpLambdaParameter parameter : getParameters()) { if(!processor.execute(parameter, state)) { return false; } } return true; } @RequiredReadAction @NotNull @Override public DotNetTypeRef toTypeRefImpl(boolean resolveFromParent) { return new CSharpLambdaTypeRef(this, null, getParameterInfos(resolveFromParent), resolveFromParent ? getReturnTypeRef() : DotNetTypeRef.AUTO_TYPE); } @NotNull @RequiredReadAction public DotNetTypeRef toTypeRefForInference() { // recursion when child lambda reference to parameter from parent lambda DotNetTypeRef returnType = RecursionManager.doPreventingRecursion(" C#lambda return type", false, this::findPossibleReturnTypeRef); if(returnType == null) { returnType = DotNetTypeRef.ERROR_TYPE; } return new CSharpLambdaTypeRef(this, null, getParameterInfos(true), returnType); } @NotNull @RequiredReadAction private DotNetTypeRef findPossibleReturnTypeRef() { PsiElement codeBlock = getCodeBlock(); if(codeBlock instanceof DotNetExpression) { return ((DotNetExpression) codeBlock).toTypeRef(false); } if(codeBlock == null) { return DotNetTypeRef.ERROR_TYPE; } final List<DotNetTypeRef> typeRefs = new ArrayList<>(); codeBlock.accept(new CSharpRecursiveElementVisitor() { @Override public void visitAnonymMethodExpression(CSharpDelegateExpressionImpl method) { // dont need check return inside anonym } @Override public void visitLambdaExpression(CSharpLambdaExpressionImpl expression) { // dont need check return inside lambda } @Override public void visitYieldStatement(CSharpYieldStatementImpl statement) { //FIXME [VISTALL] what we need to do? } @Override @RequiredReadAction public void visitReturnStatement(CSharpReturnStatementImpl statement) { CSharpImplicitReturnModel implicitReturnModel = CSharpImplicitReturnModel.getImplicitReturnModel(statement, CSharpLambdaExpressionImpl.this); DotNetExpression expression = statement.getExpression(); DotNetTypeRef expectedTypeRef; expectedTypeRef = expression == null ? new CSharpTypeRefByQName(statement, DotNetTypes.System.Void) : expression.toTypeRef(false); if(expectedTypeRef == DotNetTypeRef.ERROR_TYPE) { return; } if(implicitReturnModel == CSharpImplicitReturnModel.None) { typeRefs.add(expectedTypeRef); } else { if(DotNetTypeRefUtil.isVmQNameEqual(expectedTypeRef, statement, DotNetTypes.System.Void)) { typeRefs.add(new CSharpTypeRefByQName(statement, implicitReturnModel.getNoGenericTypeVmQName())); } else { typeRefs.add(new CSharpGenericWrapperTypeRef(new CSharpTypeRefByQName(statement, implicitReturnModel.getGenericVmQName()), expectedTypeRef)); } } } }); if(typeRefs.isEmpty()) { return new CSharpTypeRefByQName(this, DotNetTypes.System.Void); } return typeRefs.get(0); } @RequiredReadAction @NotNull @Override public CSharpSimpleParameterInfo[] getParameterInfos() { return getParameterInfos(false); } @NotNull @RequiredReadAction public CSharpSimpleParameterInfo[] getParameterInfos(boolean resolveFromParent) { CSharpLambdaParameter[] parameters = getParameters(); CSharpSimpleParameterInfo[] parameterInfos = new CSharpSimpleParameterInfo[parameters.length]; for(int i = 0; i < parameters.length; i++) { CSharpLambdaParameter parameter = parameters[i]; parameterInfos[i] = new CSharpSimpleParameterInfo(i, parameter.getName(), parameter, parameter.toTypeRef(resolveFromParent)); } return parameterInfos; } @RequiredReadAction @NotNull @Override public DotNetTypeRef getReturnTypeRef() { CSharpLambdaResolveResult type = CSharpLambdaExpressionImplUtil.resolveLeftLambdaTypeRef(this); if(type == null) { return DotNetTypeRef.UNKNOWN_TYPE; } return type.getReturnTypeRef(); } }