/* * 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.ide.completion.expected; import java.util.List; import org.jetbrains.annotations.NotNull; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveResult; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import consulo.annotations.RequiredReadAction; import consulo.csharp.lang.psi.*; import consulo.csharp.lang.psi.impl.CSharpImplicitReturnModel; import consulo.csharp.lang.psi.impl.DotNetTypes2; import consulo.csharp.lang.psi.impl.source.*; import consulo.csharp.lang.psi.impl.source.resolve.MethodResolveResult; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.MethodCalcResult; import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NCallArgument; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpTypeRefByQName; import consulo.dotnet.DotNetTypes; import consulo.dotnet.psi.DotNetExpression; import consulo.dotnet.psi.DotNetParameter; import consulo.dotnet.psi.DotNetVariable; import consulo.dotnet.resolve.DotNetTypeRef; import consulo.dotnet.util.ArrayUtil2; /** * @author VISTALL * @since 06.03.2015 */ public class ExpectedTypeVisitor extends CSharpElementVisitor { public static final Key<List<ExpectedTypeInfo>> EXPECTED_TYPE_INFOS = Key.create("ExpectedTypeInfo"); @NotNull public static List<ExpectedTypeInfo> findExpectedTypeRefs(@NotNull PsiElement psiElement) { PsiElement parent = psiElement.getParent(); ExpectedTypeVisitor expectedTypeVisitor = new ExpectedTypeVisitor(psiElement); parent.accept(expectedTypeVisitor); return ContainerUtil.filter(expectedTypeVisitor.getExpectedTypeInfos(), new Condition<ExpectedTypeInfo>() { @Override public boolean value(ExpectedTypeInfo expectedTypeInfo) { return expectedTypeInfo.getTypeRef() != DotNetTypeRef.ERROR_TYPE; } }); } private List<ExpectedTypeInfo> myExpectedTypeInfos = new SmartList<ExpectedTypeInfo>(); private PsiElement myCurrentElement; public ExpectedTypeVisitor(PsiElement currentElement) { myCurrentElement = currentElement; } public List<ExpectedTypeInfo> getExpectedTypeInfos() { return myExpectedTypeInfos; } @Override @RequiredReadAction public void visitIfStatement(CSharpIfStatementImpl statement) { DotNetExpression conditionExpression = statement.getConditionExpression(); if(conditionExpression == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(statement, DotNetTypes.System.Boolean), null)); } } @Override @RequiredReadAction public void visitAttribute(CSharpAttribute attribute) { if(attribute.getReferenceExpression() == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(attribute, DotNetTypes.System.Attribute), null)); } } @Override @RequiredReadAction public void visitWhileStatement(CSharpWhileStatementImpl statement) { DotNetExpression conditionExpression = statement.getConditionExpression(); if(conditionExpression == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(statement, DotNetTypes.System.Boolean), null)); } } @Override @RequiredReadAction public void visitThrowStatement(CSharpThrowStatementImpl statement) { DotNetExpression throwExpression = statement.getExpression(); if(throwExpression == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(statement, DotNetTypes.System.Exception), null)); } } @Override @RequiredReadAction public void visitDoWhileStatement(CSharpDoWhileStatementImpl statement) { DotNetExpression conditionExpression = statement.getConditionExpression(); if(conditionExpression == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(statement, DotNetTypes.System.Boolean), null)); } } @Override @RequiredReadAction public void visitForeachStatement(CSharpForeachStatementImpl statement) { if(statement.getIterableExpression() == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(statement, DotNetTypes2.System.Collections.IEnumerable), null)); myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(statement, DotNetTypes2.System.Collections.Generic.IEnumerable$1), null)); } } @Override @RequiredReadAction public void visitNamedFieldOrPropertySet(CSharpNamedFieldOrPropertySet parent) { CSharpReferenceExpression nameReferenceExpression = parent.getNameElement(); DotNetExpression valueExpression = parent.getValueExpression(); if(nameReferenceExpression == myCurrentElement) { if(valueExpression != null) { myExpectedTypeInfos.add(new ExpectedTypeInfo(valueExpression.toTypeRef(false), null)); } } else if(valueExpression == myCurrentElement) { PsiElement resolvedElement = nameReferenceExpression.resolve(); if(resolvedElement instanceof DotNetVariable) { myExpectedTypeInfos.add(new ExpectedTypeInfo(((DotNetVariable) resolvedElement).toTypeRef(true), resolvedElement)); } } } @Override @RequiredReadAction public void visitReturnStatement(CSharpReturnStatementImpl statement) { CSharpSimpleLikeMethodAsElement methodAsElement = PsiTreeUtil.getParentOfType(myCurrentElement, CSharpSimpleLikeMethodAsElement.class); if(methodAsElement == null) { return; } CSharpImplicitReturnModel implicitReturnModel = CSharpImplicitReturnModel.getImplicitReturnModel(statement, methodAsElement); DotNetTypeRef extractedTypeRef = implicitReturnModel.extractTypeRef(methodAsElement.getReturnTypeRef(), statement); myExpectedTypeInfos.add(new ExpectedTypeInfo(extractedTypeRef, methodAsElement)); } @Override @RequiredReadAction public void visitRefTypeExpression(CSharpRefTypeExpressionImpl expression) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(expression, DotNetTypes.System.TypedReference), null)); } @Override @RequiredReadAction public void visitRefValueExpression(CSharpRefValueExpressionImpl parent) { if(parent.getExpression() == myCurrentElement) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(parent, DotNetTypes.System.TypedReference), null)); } } @Override @RequiredReadAction public void visitAssignmentExpression(CSharpAssignmentExpressionImpl parent) { DotNetExpression[] expressions = parent.getParameterExpressions(); // <caret> = test; if(expressions.length == 1) { return; } if(expressions[0] == myCurrentElement) { DotNetExpression rightExpression = expressions[1]; DotNetTypeRef typeRef = rightExpression.toTypeRef(true); myExpectedTypeInfos.add(new ExpectedTypeInfo(typeRef, null)); } else { CSharpOperatorReferenceImpl operatorElement = parent.getOperatorElement(); ResolveResult[] resolveResults = operatorElement.multiResolve(false); for(ResolveResult resolveResult : resolveResults) { PsiElement element = resolveResult.getElement(); // stub variant if(operatorElement == element) { PsiElement typeProvider = null; DotNetExpression expression = expressions[0]; if(expression instanceof CSharpReferenceExpression) { typeProvider = ((CSharpReferenceExpression) expression).resolve(); } myExpectedTypeInfos.add(new ExpectedTypeInfo(expression.toTypeRef(false), typeProvider)); } else if(element instanceof CSharpMethodDeclaration) { if(((CSharpMethodDeclaration) element).isOperator()) { DotNetParameter[] parameters = ((CSharpMethodDeclaration) element).getParameters(); DotNetParameter parameter = ArrayUtil2.safeGet(parameters, 1); if(parameter == null) { return; } myExpectedTypeInfos.add(new ExpectedTypeInfo(parameter.toTypeRef(true), element)); } } } } } @Override @RequiredReadAction public void visitAwaitExpression(CSharpAwaitExpressionImpl expression) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(expression, DotNetTypes2.System.Threading.Tasks.Task), null)); myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(expression, DotNetTypes2.System.Threading.Tasks.Task$1), null)); } @Override @RequiredReadAction public void visitVariable(DotNetVariable variable) { myExpectedTypeInfos.add(new ExpectedTypeInfo(variable.toTypeRef(false), variable)); } @Override public void visitCallArgument(CSharpCallArgument argument) { CSharpCallArgumentListOwner callArgumentListOwner = PsiTreeUtil.getParentOfType(argument, CSharpCallArgumentListOwner.class, false); assert callArgumentListOwner != null; ResolveResult[] resolveResults = callArgumentListOwner.multiResolve(false); for(ResolveResult resolveResult : resolveResults) { if(resolveResult instanceof MethodResolveResult) { MethodCalcResult calcResult = ((MethodResolveResult) resolveResult).getCalcResult(); for(NCallArgument nCallArgument : calcResult.getArguments()) { if(nCallArgument.getCallArgument() == argument) { DotNetTypeRef parameterTypeRef = nCallArgument.getParameterTypeRef(); if(parameterTypeRef == null) { continue; } myExpectedTypeInfos.add(new ExpectedTypeInfo(parameterTypeRef, resolveResult.getElement())); } } } } } @Override public void visitNamedCallArgument(CSharpNamedCallArgument argument) { visitCallArgument(argument); } @Override @RequiredReadAction public void visitUserType(CSharpUserType parent) { PsiElement parentOfUserType = parent.getParent(); if(parentOfUserType instanceof CSharpAsExpressionImpl || parentOfUserType instanceof CSharpTypeCastExpressionImpl || parentOfUserType instanceof CSharpNewExpression || parentOfUserType instanceof CSharpRefValueExpressionImpl) { myExpectedTypeInfos.addAll(findExpectedTypeRefs(parentOfUserType)); } else if(parentOfUserType instanceof CSharpLocalVariable && parentOfUserType.getParent() instanceof CSharpCatchStatementImpl) { myExpectedTypeInfos.add(new ExpectedTypeInfo(new CSharpTypeRefByQName(parent, DotNetTypes.System.Exception), null)); } } @Override public void visitOurRefWrapExpression(CSharpOutRefWrapExpressionImpl expression) { myExpectedTypeInfos.addAll(findExpectedTypeRefs(expression)); } }