/* * 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.highlight.check.impl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.annotations.RequiredReadAction; import consulo.csharp.ide.CSharpErrorBundle; import consulo.csharp.ide.codeInsight.actions.AddModifierFix; import consulo.csharp.ide.codeInsight.actions.CastExpressionToTypeRef; import consulo.csharp.ide.codeInsight.actions.ChangeReturnToTypeRefFix; import consulo.csharp.ide.codeInsight.actions.ChangeVariableToTypeRefFix; import consulo.csharp.ide.highlight.CSharpHighlightContext; import consulo.csharp.ide.highlight.CSharpHighlightKey; import consulo.csharp.ide.highlight.check.CompilerCheck; import consulo.csharp.lang.psi.CSharpConversionMethodDeclaration; import consulo.csharp.lang.psi.CSharpMethodDeclaration; import consulo.csharp.lang.psi.CSharpModifier; import consulo.csharp.lang.psi.CSharpSimpleLikeMethodAsElement; import consulo.csharp.lang.psi.CSharpTokens; import consulo.csharp.lang.psi.CSharpTypeDeclaration; import consulo.csharp.lang.psi.CSharpTypeRefPresentationUtil; import consulo.csharp.lang.psi.impl.CSharpImplicitReturnModel; import consulo.csharp.lang.psi.impl.CSharpTypeUtil; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpArrayTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpLambdaResolveResult; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpPointerTypeRef; import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpTypeRefByQName; import consulo.csharp.lang.psi.impl.source.*; import consulo.csharp.module.extension.CSharpLanguageVersion; import consulo.csharp.module.extension.CSharpModuleUtil; import consulo.dotnet.DotNetTypes; import consulo.dotnet.psi.DotNetExpression; import consulo.dotnet.psi.DotNetGenericParameter; import consulo.dotnet.psi.DotNetLikeMethodDeclaration; import consulo.dotnet.psi.DotNetVariable; import consulo.dotnet.resolve.DotNetTypeRef; import consulo.dotnet.resolve.DotNetTypeRefUtil; import consulo.dotnet.resolve.DotNetTypeResolveResult; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.openapi.util.Trinity; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtil; import consulo.csharp.lang.CSharpCastType; /** * @author VISTALL * @since 15.05.14 */ public class CS0029 extends CompilerCheck<PsiElement> { @RequiredReadAction @Nullable @Override public CompilerCheckBuilder checkImpl(@NotNull CSharpLanguageVersion languageVersion, @NotNull CSharpHighlightContext highlightContext, @NotNull PsiElement element) { Trinity<? extends DotNetTypeRef, ? extends DotNetTypeRef, ? extends PsiElement> resolve = resolve(element); if(resolve == null) { return null; } DotNetTypeRef firstTypeRef = resolve.getFirst(); DotNetTypeRef secondTypeRef = resolve.getSecond(); if(CSharpTypeUtil.isErrorTypeRef(firstTypeRef) || CSharpTypeUtil.isErrorTypeRef(secondTypeRef)) { return null; } PsiElement elementToHighlight = resolve.getThird(); CSharpTypeUtil.InheritResult inheritResult = CSharpTypeUtil.isInheritable(firstTypeRef, secondTypeRef, element, CSharpCastType.IMPLICIT); if(!inheritResult.isSuccess()) { CompilerCheckBuilder builder = newBuilder(elementToHighlight, formatTypeRef(secondTypeRef, element), formatTypeRef(firstTypeRef, element)); if(elementToHighlight instanceof DotNetExpression) { builder.addQuickFix(new CastExpressionToTypeRef((DotNetExpression) elementToHighlight, firstTypeRef)); } if(element instanceof DotNetVariable) { builder.addQuickFix(new ChangeVariableToTypeRefFix((DotNetVariable) element, secondTypeRef)); } if(element instanceof CSharpReturnStatementImpl) { CSharpSimpleLikeMethodAsElement methodElement = PsiTreeUtil.getParentOfType(element, CSharpSimpleLikeMethodAsElement.class); if(methodElement instanceof CSharpConversionMethodDeclaration || methodElement instanceof CSharpMethodDeclaration) { builder.addQuickFix(new ChangeReturnToTypeRefFix((DotNetLikeMethodDeclaration) methodElement, secondTypeRef)); } if(CSharpModuleUtil.findLanguageVersion(element).isAtLeast(CSharpLanguageVersion._4_0)) { DotNetTypeResolveResult typeResolveResult = firstTypeRef.resolve(); PsiElement firstElement = typeResolveResult.getElement(); if(firstElement instanceof CSharpTypeDeclaration && DotNetTypes.System.Threading.Tasks.Task$1.equals(((CSharpTypeDeclaration) firstElement).getVmQName())) { DotNetGenericParameter[] genericParameters = ((CSharpTypeDeclaration) firstElement).getGenericParameters(); DotNetTypeRef genericParameterTypeRef = typeResolveResult.getGenericExtractor().extract(genericParameters[0]); if(genericParameterTypeRef != null && CSharpTypeUtil.isInheritable(genericParameterTypeRef, secondTypeRef, element)) { builder.addQuickFix(new AddModifierFix(CSharpModifier.ASYNC, methodElement)); } } } } return builder; } else if(inheritResult.isConversion()) { String text = CSharpErrorBundle.message("impicit.cast.from.0.to.1", CSharpTypeRefPresentationUtil.buildTextWithKeywordAndNull(secondTypeRef, element), CSharpTypeRefPresentationUtil.buildTextWithKeywordAndNull(firstTypeRef, element)); return newBuilder(elementToHighlight).setText(text).setHighlightInfoType(HighlightInfoType.INFORMATION).setTextAttributesKey(CSharpHighlightKey.IMPLICIT_OR_EXPLICIT_CAST); } return null; } @RequiredReadAction private Trinity<? extends DotNetTypeRef, ? extends DotNetTypeRef, ? extends PsiElement> resolve(PsiElement element) { if(element instanceof DotNetVariable) { DotNetExpression initializer = ((DotNetVariable) element).getInitializer(); if(initializer == null) { return null; } DotNetTypeRef variableTypRef = ((DotNetVariable) element).toTypeRef(false); if(variableTypRef == DotNetTypeRef.AUTO_TYPE) { return null; } DotNetTypeRef initializerTypeRef = initializer.toTypeRef(true); PsiElement parent = element.getParent(); if(parent instanceof CSharpFixedStatementImpl) { if(initializerTypeRef instanceof CSharpArrayTypeRef) { if(((CSharpArrayTypeRef) initializerTypeRef).getDimensions() == 0) { DotNetTypeRef innerTypeRef = ((CSharpArrayTypeRef) initializerTypeRef).getInnerTypeRef(); initializerTypeRef = new CSharpPointerTypeRef(element, innerTypeRef); } } } return Trinity.create(variableTypRef, initializerTypeRef, initializer); } else if(element instanceof DotNetExpression && element.getParent() instanceof CSharpMethodDeclaration) { CSharpMethodDeclaration parent = (CSharpMethodDeclaration) element.getParent(); assert parent.getCodeBlock() == element; CSharpImplicitReturnModel model = CSharpImplicitReturnModel.None; if(parent.hasModifier(CSharpModifier.ASYNC)) { model = CSharpImplicitReturnModel.Async; } DotNetTypeRef returnTypeRef = model.extractTypeRef(parent.getReturnTypeRef(), element); return Trinity.create(returnTypeRef, ((DotNetExpression) element).toTypeRef(true), element); } else if(element instanceof CSharpAssignmentExpressionImpl) { CSharpOperatorReferenceImpl operatorElement = ((CSharpAssignmentExpressionImpl) element).getOperatorElement(); if(operatorElement.getOperatorElementType() != CSharpTokens.EQ) { return null; } DotNetExpression[] expressions = ((CSharpAssignmentExpressionImpl) element).getParameterExpressions(); if(expressions.length != 2) { return null; } return Trinity.create(expressions[0].toTypeRef(true), expressions[1].toTypeRef(true), expressions[1]); } else if(element instanceof CSharpWhileStatementImpl) { DotNetExpression conditionExpression = ((CSharpWhileStatementImpl) element).getConditionExpression(); if(conditionExpression == null) { return null; } return Trinity.create(new CSharpTypeRefByQName(element, DotNetTypes.System.Boolean), conditionExpression.toTypeRef(true), conditionExpression); } else if(element instanceof CSharpDoWhileStatementImpl) { DotNetExpression conditionExpression = ((CSharpDoWhileStatementImpl) element).getConditionExpression(); if(conditionExpression == null) { return null; } return Trinity.create(new CSharpTypeRefByQName(element, DotNetTypes.System.Boolean), conditionExpression.toTypeRef(true), conditionExpression); } else if(element instanceof CSharpIfStatementImpl) { DotNetExpression conditionExpression = ((CSharpIfStatementImpl) element).getConditionExpression(); if(conditionExpression == null) { return null; } return Trinity.create(new CSharpTypeRefByQName(element, DotNetTypes.System.Boolean), conditionExpression.toTypeRef(true), conditionExpression); } else if(element instanceof CSharpRefTypeExpressionImpl) { DotNetExpression expression = ((CSharpRefTypeExpressionImpl) element).getExpression(); if(expression == null) { return null; } return Trinity.create(new CSharpTypeRefByQName(element, DotNetTypes.System.TypedReference), expression.toTypeRef(true), expression); } else if(element instanceof CSharpSwitchLabelStatementImpl) { DotNetExpression expression = ((CSharpSwitchLabelStatementImpl) element).getExpression(); if(expression == null) { return null; } PsiElement parent = element.getParent().getParent(); if(!(parent instanceof CSharpSwitchStatementImpl)) { return null; } DotNetExpression switchExpression = ((CSharpSwitchStatementImpl) parent).getExpression(); if(switchExpression == null) { return null; } return Trinity.create(switchExpression.toTypeRef(true), expression.toTypeRef(true), expression); } else if(element instanceof CSharpRefValueExpressionImpl) { DotNetExpression expression = ((CSharpRefValueExpressionImpl) element).getExpression(); if(expression == null) { return null; } return Trinity.create(new CSharpTypeRefByQName(element, DotNetTypes.System.TypedReference), expression.toTypeRef(true), expression); } else if(element instanceof CSharpLambdaExpressionImpl) { CSharpLambdaExpressionImpl lambdaExpression = (CSharpLambdaExpressionImpl) element; DotNetTypeRef typeRefOfLambda = lambdaExpression.toTypeRef(true); if(typeRefOfLambda == DotNetTypeRef.ERROR_TYPE) { return null; } DotNetTypeResolveResult typeResolveResult = typeRefOfLambda.resolve(); if(!(typeResolveResult instanceof CSharpLambdaResolveResult)) { return null; } PsiElement singleExpression = lambdaExpression.getCodeBlock(); if(singleExpression instanceof DotNetExpression) { DotNetTypeRef returnTypeRef = ((CSharpLambdaResolveResult) typeResolveResult).getReturnTypeRef(); CSharpImplicitReturnModel model = CSharpImplicitReturnModel.None; if(lambdaExpression.hasModifier(CSharpModifier.ASYNC)) { model = CSharpImplicitReturnModel.Async; } returnTypeRef = model.extractTypeRef(returnTypeRef, element); // void type allow any type if used expression body if(DotNetTypeRefUtil.isVmQNameEqual(returnTypeRef, element, DotNetTypes.System.Void)) { return null; } return Trinity.create(returnTypeRef, ((DotNetExpression) singleExpression).toTypeRef(true), singleExpression); } } else if(element instanceof CSharpReturnStatementImpl) { CSharpSimpleLikeMethodAsElement pseudoMethod = PsiTreeUtil.getParentOfType(element, CSharpSimpleLikeMethodAsElement.class); if(pseudoMethod == null) { return null; } DotNetTypeRef expected = pseudoMethod.getReturnTypeRef(); if(expected == DotNetTypeRef.UNKNOWN_TYPE) { return null; } DotNetTypeRef actual; DotNetExpression expression = ((CSharpReturnStatementImpl) element).getExpression(); if(expression == null) { actual = new CSharpTypeRefByQName(element, DotNetTypes.System.Void); } else { actual = expression.toTypeRef(true); } CSharpImplicitReturnModel implicitReturnModel = CSharpImplicitReturnModel.getImplicitReturnModel((CSharpReturnStatementImpl) element, pseudoMethod); DotNetTypeRef typeRef = implicitReturnModel.extractTypeRef(expected, element); if(typeRef != DotNetTypeRef.ERROR_TYPE) { expected = typeRef; } return Trinity.create(expected, actual, ObjectUtil.notNull(expression, element)); } return null; } }