diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java index 69db5d716a75..cd119a57d030 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java @@ -355,29 +355,31 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo } @Nullable - public static String getSignature(PsiMember member) { + private static String getSignature(@Nullable PsiMember member) { if (member instanceof PsiClass) { return ((PsiClass)member).getQualifiedName(); } if (member instanceof PsiField) { - return getSignature(member.getContainingClass()) + "#" + member.getName(); + String containingClass = getSignature(member.getContainingClass()); + return containingClass == null ? null : containingClass + "#" + member.getName(); } if (member instanceof PsiMethod) { final PsiMethod method = (PsiMethod)member; + String containingClass = getSignature(member.getContainingClass()); + if (containingClass == null) return null; + StringBuilder buf = new StringBuilder(); - buf.append(getSignature(method.getContainingClass())); + buf.append(containingClass); buf.append('#'); buf.append(method.getName()); buf.append('('); - final PsiType[] params = method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes(); - for (PsiType type : params) { + for (PsiType type : method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes()) { buf.append(type.getCanonicalText()); buf.append(";"); } buf.append(')'); return buf.toString(); } - assert false; return null; } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java index 2fc06a2dabd5..fb415ec2fbdb 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameCompletionContributor.java @@ -134,6 +134,10 @@ public class JavaClassNameCompletionContributor extends CompletionContributor { }); } + static LookupElement highlightIfNeeded(JavaPsiClassReferenceElement element, CompletionParameters parameters) { + return JavaCompletionUtil.highlightIfNeeded(null, element, element.getObject(), parameters.getPosition()); + } + public static JavaPsiClassReferenceElement createClassLookupItem(final PsiClass psiClass, final boolean inJavaContext) { return AllClassesGetter.createLookupItem(psiClass, inJavaContext ? JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER : AllClassesGetter.TRY_SHORTENING); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index fba10a633432..e12fad06e77f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -264,7 +264,7 @@ public class JavaCompletionContributor extends CompletionContributor { return PsiUtilCore.findLanguageFromElement(position).isKindOf(JavaLanguage.INSTANCE); } - public static void addAllClasses(CompletionParameters parameters, + public static void addAllClasses(final CompletionParameters parameters, final CompletionResultSet result, final InheritorsHolder inheritors) { if (!isClassNamePossible(parameters) || !mayStartClassName(result)) { @@ -276,7 +276,7 @@ public class JavaCompletionContributor extends CompletionContributor { @Override public void consume(LookupElement element) { if (!inheritors.alreadyProcessed(element)) { - result.addElement(element); + result.addElement(JavaClassNameCompletionContributor.highlightIfNeeded((JavaPsiClassReferenceElement)element, parameters)); } } }); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 17cb8724dc56..6c22727a15df 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -22,6 +22,7 @@ import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor; import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; import com.intellij.codeInsight.guess.GuessManager; import com.intellij.codeInsight.lookup.*; +import com.intellij.codeInspection.java15api.Java15APIUsageInspectionBase; import com.intellij.lang.StdLanguages; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.diagnostic.Logger; @@ -50,6 +51,7 @@ import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.ui.JBColor; import com.intellij.util.IncorrectOperationException; import com.intellij.util.NullableFunction; import com.intellij.util.PairConsumer; @@ -60,7 +62,9 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.awt.*; import java.util.*; +import java.util.List; import static com.intellij.patterns.PlatformPatterns.psiElement; @@ -383,7 +387,7 @@ public class JavaCompletionUtil { } mentioned.add(CompletionUtil.getOriginalOrSelf((PsiMember)o)); } - set.add(highlightIfNeeded(qualifierType, castQualifier(item, castItem, plainQualifier, processor), o)); + set.add(highlightIfNeeded(qualifierType, castQualifier(item, castItem, plainQualifier, processor), o, element)); } } @@ -503,11 +507,37 @@ public class JavaCompletionUtil { return type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type; } - public static LookupElement highlightIfNeeded(PsiType qualifierType, LookupElement item, Object object) { - return containsMember(qualifierType, object) ? highlight(item) : item; + @NotNull + public static LookupElement highlightIfNeeded(@Nullable PsiType qualifierType, + @NotNull LookupElement item, + @NotNull Object object, + @NotNull PsiElement place) { + final boolean bold = containsMember(qualifierType, object); + boolean red = false; + if (object instanceof PsiMember) { + red = Java15APIUsageInspectionBase.isForbiddenApiUsage((PsiMember)object, PsiUtil.getLanguageLevel(place)); + } + LookupElement result = item; + if (bold || red) { + final Color fg = red ? JBColor.RED : null; + result = LookupElementDecorator.withRenderer(result, new LookupElementRenderer>() { + @Override + public void renderElement(LookupElementDecorator element, LookupElementPresentation presentation) { + element.getDelegate().renderElement(presentation); + presentation.setItemTextBold(bold); + if (fg != null) { + presentation.setItemTextForeground(fg); + } + } + }); + } + if (bold) { + result = PrioritizedLookupElement.withExplicitProximity(result, 1); + } + return result; } - public static boolean containsMember(PsiType qualifierType, Object object) { + public static boolean containsMember(@Nullable PsiType qualifierType, @NotNull Object object) { if (qualifierType instanceof PsiArrayType && object instanceof PsiMember) { //length and clone() PsiFile file = ((PsiMember)object).getContainingFile(); if (file == null || file.getVirtualFile() == null) { //yes, they're a bit dummy diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java index ceebdecdf8c3..128a7a6622f9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java @@ -20,7 +20,6 @@ import com.intellij.codeInsight.completion.impl.BetterPrefixMatcher; import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; import com.intellij.codeInsight.lookup.AutoCompletionPolicy; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -145,7 +144,7 @@ public class JavaNoVariantsDelegator extends CompletionContributor { if (ref != null) { for (final LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters, result.getPrefixMatcher())) { - qualifiedCollector.addElement(new JavaChainLookupElement(base, item)); + qualifiedCollector.addElement(JavaCompletionUtil.highlightIfNeeded(null, new JavaChainLookupElement(base, item), item.getObject(), position)); } } } @@ -178,7 +177,7 @@ public class JavaNoVariantsDelegator extends CompletionContributor { return allClasses; } - private static void suggestNonImportedClasses(CompletionParameters parameters, final CompletionResultSet result, @Nullable final InheritorsHolder inheritorsHolder) { + private static void suggestNonImportedClasses(final CompletionParameters parameters, final CompletionResultSet result, @Nullable final InheritorsHolder inheritorsHolder) { JavaClassNameCompletionContributor.addAllClasses(parameters, true, result.getPrefixMatcher(), new Consumer() { @Override @@ -189,6 +188,7 @@ public class JavaNoVariantsDelegator extends CompletionContributor { JavaPsiClassReferenceElement classElement = element.as(JavaPsiClassReferenceElement.CLASS_CONDITION_KEY); if (classElement != null) { classElement.setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + element = JavaClassNameCompletionContributor.highlightIfNeeded(classElement, parameters); } result.addElement(element); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/HeavyNormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/HeavyNormalCompletionTest.groovy index 5d0bcd4eaa8c..72aa72138dff 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/HeavyNormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/HeavyNormalCompletionTest.groovy @@ -6,6 +6,7 @@ package com.intellij.codeInsight.completion import com.intellij.JavaTestUtil import com.intellij.codeInsight.generation.OverrideImplementExploreUtil import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.lookup.LookupManager import com.intellij.codeInsight.lookup.impl.LookupImpl import com.intellij.openapi.application.ApplicationManager @@ -18,14 +19,17 @@ import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.SourceFolder import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.pom.java.LanguageLevel import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiClass import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiManager import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.PsiTreeUtil +import com.intellij.testFramework.IdeaTestUtil import com.intellij.testFramework.PsiTestUtil import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase +import com.intellij.ui.JBColor import org.jetbrains.annotations.NotNull /** @@ -171,4 +175,33 @@ public class Test { assert oldCount == tracker.javaStructureModificationCount } + public void testForbiddenApiVariants() { + IdeaTestUtil.setModuleLanguageLevel(myModule, LanguageLevel.JDK_1_4) + myFixture.addClass("""\ +package java.nio.channels; +public class SocketChannel { + public SocketChannel shutdownInput() {} + public boolean isConnected(); +}""") + myFixture.addClass("package java.nio.channels; public class AsynchronousServerSocketChannel { }") + + myFixture.configureByText 'a.java', 'class Foo {{ new SocketChannex }}' + myFixture.completeBasic() + def p = LookupElementPresentation.renderElement(myFixture.lookup.items[0]) + assert p.itemText == 'SocketChannel' + assert p.itemTextForeground == JBColor.foreground() + + p = LookupElementPresentation.renderElement(myFixture.lookup.items.find { it.lookupString == 'AsynchronousServerSocketChannel' }) + assert p.itemTextForeground == JBColor.RED + + myFixture.type('\n.s') + myFixture.completeBasic() + p = LookupElementPresentation.renderElement(myFixture.lookup.items[0]) + assert p.itemText == 'shutdownInput' + assert p.itemTextForeground == JBColor.RED + + p = LookupElementPresentation.renderElement(myFixture.lookup.items.find { it.lookupString == 'isConnected' }) + assert p.itemTextForeground == JBColor.foreground() + } + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java index b30d8f997746..6accb46d32e7 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMainCompletionProvider.java @@ -252,7 +252,7 @@ public class GrMainCompletionProvider extends CompletionProvider