diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java index 54ca42689ed..eac40c20019 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java @@ -18,22 +18,29 @@ package org.jetbrains.jet.jvm.compiler; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileVisitor; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.ConfigurationKind; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.TestJdkKind; +import org.jetbrains.jet.cli.jvm.compiler.CoreExternalAnnotationsManager; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.di.InjectorForJavaSemanticServices; import org.jetbrains.jet.lang.BuiltinsScopeExtensionMode; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.lang.resolve.lazy.KotlinTestWithEnvironment; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.resolve.DescriptorRenderer; @@ -46,6 +53,7 @@ import java.util.regex.Pattern; public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment { private VirtualFile kotlinAnnotationsRoot; + private VirtualFile ideaAnnotationsRoot; @Override protected JetCoreEnvironment createEnvironment() { @@ -57,6 +65,7 @@ public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment { protected void setUp() throws Exception { super.setUp(); kotlinAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("file://jdk-annotations"); + ideaAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("jar://ideaSDK/lib/jdkAnnotations.jar!/"); } public void testNoErrorsInAlternativeSignatures() { @@ -112,6 +121,85 @@ public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment { } } + private CoreExternalAnnotationsManager createFakeAnnotationsManager(VirtualFile annotationsRoot) { + CoreExternalAnnotationsManager annotationsManager = new CoreExternalAnnotationsManager(PsiManager.getInstance(getProject())); + annotationsManager.addExternalAnnotationsRoot(annotationsRoot); + return annotationsManager; + } + + public void testAllIdeaJdkAnnotationsAreReflected() { + JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(getProject()); + GlobalSearchScope allScope = GlobalSearchScope.allScope(getProject()); + + final CoreExternalAnnotationsManager kotlinFakeAnnotationsManager = createFakeAnnotationsManager(kotlinAnnotationsRoot); + final CoreExternalAnnotationsManager ideaFakeAnnotationsManager = createFakeAnnotationsManager(ideaAnnotationsRoot); + + final Set declarationsWithMissingAnnotations = Sets.newLinkedHashSet(); + + for (FqName classFqName : getAffectedClasses(ideaAnnotationsRoot)) { + if (new FqName("org.jdom").equals(classFqName.parent())) continue; // filter unrelated jdom annotations + + PsiClass psiClass = javaPsiFacade.findClass(classFqName.getFqName(), allScope); + assertNotNull("Class has annotation, but it is not found: " + classFqName, psiClass); + + psiClass.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + if (method.getReturnType() != null) { // disabled for constructors + check(method, method); + } + } + + @Override + public void visitField(PsiField field) { + super.visitField(field); + check(field, field); + } + + @Override + public void visitParameter(PsiParameter parameter) { + super.visitParameter(parameter); + PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class); + assert method != null; + if (method.getReturnType() != null) { // disabled for constructors + check(parameter, method); + } + } + + private void check(@NotNull PsiModifierListOwner ideaOwner, @NotNull PsiModifierListOwner kotlinOwner) { + if (hasAnnotation(ideaFakeAnnotationsManager, ideaOwner, AnnotationUtil.NOT_NULL)) { + boolean kotlinHasNotNull = hasAnnotation(kotlinFakeAnnotationsManager, kotlinOwner, AnnotationUtil.NOT_NULL); + boolean kotlinHasKotlinSignature = hasAnnotation(kotlinFakeAnnotationsManager, kotlinOwner, + JvmStdlibNames.KOTLIN_SIGNATURE.getFqName().getFqName()); + if (kotlinOwner == ideaOwner && kotlinHasNotNull || kotlinHasKotlinSignature) { + // good + } + else { + declarationsWithMissingAnnotations.add(kotlinOwner); + } + } + } + + private boolean hasAnnotation( + @NotNull CoreExternalAnnotationsManager annotationsManager, + @NotNull PsiModifierListOwner owner, + @NotNull String annotationFqName + ) { + return annotationsManager.findExternalAnnotation(owner, annotationFqName) != null; + } + }); + } + + if (!declarationsWithMissingAnnotations.isEmpty()) { + StringBuilder builder = new StringBuilder("Annotations missing for JDK items:\n"); + for (PsiModifierListOwner declaration : declarationsWithMissingAnnotations) { + builder.append(PsiFormatUtil.getExternalName(declaration)).append("\n"); + } + fail(builder.toString()); + } + } + private static Iterable getAffectedClasses(final VirtualFile root) { final Set result = Sets.newLinkedHashSet(); VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {