/* * 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; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NotNull; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.HighlightVisitor; import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder; import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.ResolveResult; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiUtilCore; import consulo.annotations.RequiredReadAction; import consulo.csharp.ide.CSharpErrorBundle; import consulo.csharp.ide.codeInsight.actions.ConvertToNormalCallFix; import consulo.csharp.ide.highlight.util.ConstructorHighlightUtil; import consulo.csharp.ide.highlight.util.GenericParameterHighlightUtil; import consulo.csharp.lang.psi.*; import consulo.csharp.lang.psi.impl.source.CSharpIndexAccessExpressionImpl; import consulo.csharp.lang.psi.impl.source.CSharpLinqExpressionImpl; import consulo.csharp.lang.psi.impl.source.CSharpMethodCallExpressionImpl; import consulo.csharp.lang.psi.impl.source.CSharpOperatorReferenceImpl; 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.operatorResolving.ImplicitCastInfo; import consulo.csharp.lang.psi.impl.source.resolve.util.CSharpMethodImplUtil; import consulo.csharp.lang.psi.impl.source.resolve.util.CSharpResolveUtil; import consulo.dotnet.psi.DotNetExpression; import consulo.dotnet.psi.DotNetGenericParameter; import consulo.dotnet.psi.DotNetParameter; /** * @author VISTALL * @since 28.11.13. */ public class CSharpHighlightVisitor extends CSharpElementVisitor implements HighlightVisitor { private HighlightInfoHolder myHighlightInfoHolder; private TIntHashSet myProcessedLines = new TIntHashSet(); private Document myDocument; @Override public boolean suitableForFile(@NotNull PsiFile psiFile) { return psiFile instanceof CSharpFile; } @Override public void visit(@NotNull PsiElement element) { element.accept(this); } @Override public void visitElement(PsiElement element) { ProgressIndicatorProvider.checkCanceled(); IElementType elementType = element.getNode().getElementType(); if(CSharpSoftTokens.ALL.contains(elementType)) { myHighlightInfoHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range(element).textAttributes(CSharpHighlightKey.SOFT_KEYWORD).create()); } else if(PsiUtilCore.getElementType(element) == CSharpTokens.NON_ACTIVE_SYMBOL) { if(myDocument == null) { return; } int lineNumber = myDocument.getLineNumber(element.getTextOffset()); if(!myProcessedLines.contains(lineNumber)) { myProcessedLines.add(lineNumber); TextRange textRange = new TextRange(myDocument.getLineStartOffset(lineNumber), myDocument.getLineEndOffset(lineNumber)); myHighlightInfoHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range(textRange).textAttributes(CSharpHighlightKey.DISABLED_BLOCK).create()); } } } @Override @RequiredReadAction public void visitIdentifier(CSharpIdentifier identifier) { PsiElement parent = identifier.getParent(); CSharpHighlightUtil.highlightNamed(myHighlightInfoHolder, parent, identifier, null); } @Override @RequiredReadAction public void visitGenericParameter(DotNetGenericParameter parameter) { super.visitGenericParameter(parameter); GenericParameterHighlightUtil.checkInAndOutModifiers(parameter, myHighlightInfoHolder); } @Override @RequiredReadAction public void visitGenericConstraint(CSharpGenericConstraint constraint) { super.visitGenericConstraint(constraint); DotNetGenericParameter resolve = constraint.resolve(); CSharpReferenceExpression reference = constraint.getGenericParameterReference(); assert reference != null; if(resolve != null) { CSharpHighlightUtil.highlightNamed(myHighlightInfoHolder, resolve, reference, null); } else { myHighlightInfoHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(reference).create()); } } @Override @RequiredReadAction public void visitConstructorDeclaration(CSharpConstructorDeclaration declaration) { super.visitConstructorDeclaration(declaration); myHighlightInfoHolder.add(ConstructorHighlightUtil.checkConstructorDeclaration(declaration)); } @Override @RequiredReadAction public void visitTypeDeclaration(CSharpTypeDeclaration declaration) { super.visitTypeDeclaration(declaration); } @Override @RequiredReadAction public void visitFieldDeclaration(CSharpFieldDeclaration declaration) { super.visitFieldDeclaration(declaration); } @Override @RequiredReadAction public void visitLocalVariable(CSharpLocalVariable variable) { super.visitLocalVariable(variable); } @Override @RequiredReadAction public void visitEnumConstantDeclaration(CSharpEnumConstantDeclaration declaration) { super.visitEnumConstantDeclaration(declaration); } @Override @RequiredReadAction public void visitTypeDefStatement(CSharpTypeDefStatement statement) { super.visitTypeDefStatement(statement); } @Override @RequiredReadAction public void visitParameter(DotNetParameter parameter) { super.visitParameter(parameter); } @Override @RequiredReadAction public void visitPropertyDeclaration(CSharpPropertyDeclaration declaration) { super.visitPropertyDeclaration(declaration); } @Override @RequiredReadAction public void visitEventDeclaration(CSharpEventDeclaration declaration) { super.visitEventDeclaration(declaration); } @Override @RequiredReadAction public void visitMethodDeclaration(CSharpMethodDeclaration declaration) { super.visitMethodDeclaration(declaration); } @Override public void visitLinqExpression(CSharpLinqExpressionImpl expression) { super.visitLinqExpression(expression); myHighlightInfoHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION).range(expression).textAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT).create()); } @Override @RequiredReadAction public void visitLinqVariable(CSharpLinqVariable variable) { super.visitLinqVariable(variable); CSharpHighlightUtil.highlightNamed(myHighlightInfoHolder, variable, variable.getNameIdentifier(), null); } @Override @RequiredReadAction public void visitIndexAccessExpression(CSharpIndexAccessExpressionImpl expression) { super.visitIndexAccessExpression(expression); highlightMaybeImplicit(expression); } @Override @RequiredReadAction public void visitReferenceExpression(CSharpReferenceExpression expression) { super.visitReferenceExpression(expression); PsiElement referenceElement = expression.getReferenceElement(); if(referenceElement == null || expression.isSoft()) { return; } highlightResolvedTarget(expression, referenceElement); } @Override @RequiredReadAction public void visitMethodCallExpression(CSharpMethodCallExpressionImpl expression) { super.visitMethodCallExpression(expression); highlightMaybeImplicit(expression); } @Override @RequiredReadAction public void visitOperatorReference(CSharpOperatorReferenceImpl referenceExpression) { super.visitOperatorReference(referenceExpression); highlightMaybeImplicit(referenceExpression); } @RequiredReadAction private void highlightResolvedTarget(@NotNull PsiReference reference, @NotNull PsiElement referenceElement) { PsiElement resolved = reference.resolve(); if(resolved != null) { HighlightInfo highlightInfo = CSharpHighlightUtil.highlightNamed(myHighlightInfoHolder, resolved, referenceElement, (PsiElement) reference); if(highlightInfo != null && CSharpMethodImplUtil.isExtensionWrapper(resolved)) { QuickFixAction.registerQuickFixAction(highlightInfo, ConvertToNormalCallFix.INSTANCE); } } } @RequiredReadAction private void highlightMaybeImplicit(@NotNull CSharpCallArgumentListOwner scope) { MethodCalcResult methodCalcResult = null; ResolveResult[] resolveResults = scope.multiResolve(false); ResolveResult firstValidResult = CSharpResolveUtil.findFirstValidResult(resolveResults); if(firstValidResult != null) { if(firstValidResult instanceof MethodResolveResult) { methodCalcResult = ((MethodResolveResult) firstValidResult).getCalcResult(); } } if(methodCalcResult == null) { return; } for(NCallArgument nCallArgument : methodCalcResult.getArguments()) { CSharpCallArgument callArgument = nCallArgument.getCallArgument(); if(callArgument == null) { continue; } DotNetExpression argumentExpression = callArgument.getArgumentExpression(); if(argumentExpression == null) { continue; } ImplicitCastInfo implicitCastInfo = nCallArgument.getUserData(ImplicitCastInfo.IMPLICIT_CAST_INFO); if(implicitCastInfo != null) { String text = CSharpErrorBundle.message("impicit.cast.from.0.to.1", CSharpTypeRefPresentationUtil.buildTextWithKeyword(implicitCastInfo.getFromTypeRef(), scope), CSharpTypeRefPresentationUtil.buildTextWithKeyword(implicitCastInfo.getToTypeRef(), scope)); HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION); builder = builder.range(argumentExpression.getTextRange()); builder = builder.descriptionAndTooltip(text); builder = builder.textAttributes(CSharpHighlightKey.IMPLICIT_OR_EXPLICIT_CAST); myHighlightInfoHolder.add(builder.create()); } } } @Override public boolean analyze(@NotNull PsiFile psiFile, boolean b, @NotNull HighlightInfoHolder highlightInfoHolder, @NotNull Runnable runnable) { myHighlightInfoHolder = highlightInfoHolder; myProcessedLines.clear(); myDocument = PsiDocumentManager.getInstance(psiFile.getProject()).getCachedDocument(psiFile); runnable.run(); myDocument = null; return true; } @NotNull @Override public HighlightVisitor clone() { return new CSharpHighlightVisitor(); } @Override public int order() { return 1; } }