diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java index c9230625eecd..a500163506c1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java @@ -20,7 +20,6 @@ import com.intellij.codeInspection.dataFlow.ContractInference; import com.intellij.codeInspection.dataFlow.MethodContract; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiAnnotation; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.util.PsiUtil; @@ -34,16 +33,10 @@ import static com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer.ORG_JETBR public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { - @NotNull - private static PsiModifierListOwner preferCompiledElement(@NotNull PsiModifierListOwner element) { - PsiElement original = element.getOriginalElement(); - return original instanceof PsiModifierListOwner ? (PsiModifierListOwner)original : element; - } - @Nullable @Override public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) { - listOwner = preferCompiledElement(listOwner); + listOwner = BaseExternalAnnotationsManager.preferCompiledElement(listOwner); PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotation(listOwner, annotationFQN); if (fromBytecode != null) { return fromBytecode; @@ -66,7 +59,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { @NotNull @Override public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) { - listOwner = preferCompiledElement(listOwner); + listOwner = BaseExternalAnnotationsManager.preferCompiledElement(listOwner); List result = ContainerUtil.newArrayList(); PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotations(listOwner); for (PsiAnnotation annotation : fromBytecode) { diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java index 966504719826..ab7a865206ab 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java @@ -70,6 +70,12 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations return PsiFormatUtil.getExternalName(listOwner, showParamName, Integer.MAX_VALUE); } + @NotNull + static PsiModifierListOwner preferCompiledElement(@NotNull PsiModifierListOwner element) { + PsiElement original = element.getOriginalElement(); + return original instanceof PsiModifierListOwner ? (PsiModifierListOwner)original : element; + } + protected abstract boolean hasAnyAnnotationsRoots(); @Override @@ -213,7 +219,8 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations @Override @Nullable - public List findExternalAnnotationsFiles(@NotNull PsiModifierListOwner listOwner) { + public List findExternalAnnotationsFiles(@NotNull PsiModifierListOwner _listOwner) { + final PsiModifierListOwner listOwner = preferCompiledElement(_listOwner); final PsiFile containingFile = listOwner.getContainingFile(); if (!(containingFile instanceof PsiJavaFile)) { return null; @@ -235,10 +242,6 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } } - if (virtualFile == null) { - return null; - } - Set possibleAnnotationsXmls = new THashSet(); for (VirtualFile root : getExternalAnnotationsRoots(virtualFile)) { final VirtualFile ext = root.findFileByRelativePath(packageName.replace('.', '/') + "/" + ANNOTATIONS_XML); diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index 09b0e28aa3fd..84f706448ce2 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -32,6 +32,7 @@ + diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java index 8cde2d7f1bc2..e64b13e36848 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java @@ -18,6 +18,7 @@ package com.intellij.codeInspection.bytecodeAnalysis; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.codeInsight.InferredAnnotationsManager; +import com.intellij.codeInsight.daemon.GutterMark; import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; @@ -26,6 +27,8 @@ import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -36,10 +39,15 @@ import com.intellij.psi.util.PsiFormatUtil; import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.util.AsynchConsumer; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.java.decompiler.IdeaDecompiler; import java.security.MessageDigest; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -48,27 +56,24 @@ import java.util.List; public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestCase { public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName(); - private InferredAnnotationsManager myInferredAnnotationsManager; - private ExternalAnnotationsManager myExternalAnnotationsManager; private MessageDigest myMessageDigest; private List diffs = new ArrayList(); @Override protected void setUp() throws Exception { super.setUp(); - - setUpLibraries(); - setUpExternalUpAnnotations(); - - myInferredAnnotationsManager = InferredAnnotationsManager.getInstance(myModule.getProject()); - myExternalAnnotationsManager = ExternalAnnotationsManager.getInstance(myModule.getProject()); myMessageDigest = BytecodeAnalysisConverter.getMessageDigest(); } - private void setUpLibraries() { + @NotNull + private static String getLibDirPath() { VirtualFile lib = LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManagerEx.getTestDataPath() + "/../../../lib"); assertNotNull(lib); - PsiTestUtil.addLibrary(myModule, "velocity", lib.getPath(), new String[]{"/velocity.jar!/"}, new String[]{}); + return lib.getPath(); + } + + private void setUpLibraries() { + PsiTestUtil.addLibrary(myModule, "velocity", getLibDirPath(), new String[]{"/velocity.jar!/"}, new String[]{}); } private void setUpExternalUpAnnotations() { @@ -103,7 +108,46 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC annotationsDir.refresh(false, true); } + private void openDecompiledClass(String name) { + PsiClass psiClass = JavaPsiFacade.getInstance(getProject()).findClass(name, GlobalSearchScope.allScope(getProject())); + assertNotNull(psiClass); + myFixture.openFileInEditor(psiClass.getContainingFile().getVirtualFile()); + + String documentText = myFixture.getEditor().getDocument().getText(); + assertTrue(documentText, documentText.startsWith(IdeaDecompiler.BANNER)); + } + + public void testInferredAnnoGutter() { + setUpLibraries(); + openDecompiledClass("org.apache.velocity.util.ExceptionUtils"); + checkHasGutter("@org.jetbrains.annotations.Contract("null,_,_->null")"); + } + + public void testExternalAnnoGutter() { + setUpExternalUpAnnotations(); + openDecompiledClass("java.lang.Boolean"); + checkHasGutter("@org.jetbrains.annotations.Contract("null->false") "); + } + + private void checkHasGutter(final String expectedText) { + Collection gutters = ContainerUtil.mapNotNull(myFixture.findAllGutters(), new Function() { + @Override + public String fun(GutterMark mark) { + return mark.getTooltipText(); + } + }); + String contractMark = ContainerUtil.find(gutters, new Condition() { + @Override + public boolean value(String mark) { + return mark.contains(expectedText); + } + }); + assertNotNull(StringUtil.join(gutters, "\n"), contractMark); + } + public void testSdkAndLibAnnotations() { + setUpLibraries(); + setUpExternalUpAnnotations(); final PsiPackage rootPackage = JavaPsiFacade.getInstance(getProject()).findPackage(""); assert rootPackage != null; @@ -137,10 +181,8 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC { // @NotNull method - String externalNotNullMethodAnnotation = - myExternalAnnotationsManager.findExternalAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; - String inferredNotNullMethodAnnotation = - myInferredAnnotationsManager.findInferredAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; + String externalNotNullMethodAnnotation = findExternalAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; + String inferredNotNullMethodAnnotation = findInferredAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; if (!externalNotNullMethodAnnotation.equals(inferredNotNullMethodAnnotation)) { diffs.add(methodKey + ": " + externalNotNullMethodAnnotation + " != " + inferredNotNullMethodAnnotation); @@ -149,10 +191,8 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC { // @Nullable method - String externalNullableMethodAnnotation = - myExternalAnnotationsManager.findExternalAnnotation(method, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; - String inferredNullableMethodAnnotation = - myInferredAnnotationsManager.findInferredAnnotation(method, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; + String externalNullableMethodAnnotation = findExternalAnnotation(method, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; + String inferredNullableMethodAnnotation = findInferredAnnotation(method, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; if (!externalNullableMethodAnnotation.equals(inferredNullableMethodAnnotation)) { diffs.add(methodKey + ": " + externalNullableMethodAnnotation + " != " + inferredNullableMethodAnnotation); @@ -164,10 +204,8 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC { // @NotNull parameter - String externalNotNull = - myExternalAnnotationsManager.findExternalAnnotation(parameter, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; - String inferredNotNull = - myInferredAnnotationsManager.findInferredAnnotation(parameter, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; + String externalNotNull = findExternalAnnotation(parameter, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; + String inferredNotNull = findInferredAnnotation(parameter, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; if (!externalNotNull.equals(inferredNotNull)) { diffs.add(parameterKey + ": " + externalNotNull + " != " + inferredNotNull); } @@ -175,10 +213,8 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC { // @Nullable parameter - String externalNullable = - myExternalAnnotationsManager.findExternalAnnotation(parameter, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; - String inferredNullable = - myInferredAnnotationsManager.findInferredAnnotation(parameter, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; + String externalNullable = findExternalAnnotation(parameter, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; + String inferredNullable = findInferredAnnotation(parameter, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; if (!externalNullable.equals(inferredNullable)) { diffs.add(parameterKey + ": " + externalNullable + " != " + inferredNullable); } @@ -186,10 +222,8 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC } // @Contract - PsiAnnotation externalContractAnnotation = - myExternalAnnotationsManager.findExternalAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); - PsiAnnotation inferredContractAnnotation = - myInferredAnnotationsManager.findInferredAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); + PsiAnnotation externalContractAnnotation = findExternalAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); + PsiAnnotation inferredContractAnnotation = findInferredAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); String externalContractAnnotationString = externalContractAnnotation == null ? "null" : "@Contract(" + AnnotationUtil.getStringAttributeValue(externalContractAnnotation, null) + ")"; @@ -202,4 +236,11 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC } + private PsiAnnotation findInferredAnnotation(PsiModifierListOwner owner, String fqn) { + return InferredAnnotationsManager.getInstance(myModule.getProject()).findInferredAnnotation(owner, fqn); + } + + private PsiAnnotation findExternalAnnotation(PsiModifierListOwner owner, String fqn) { + return ExternalAnnotationsManager.getInstance(myModule.getProject()).findExternalAnnotation(owner, fqn); + } }