Added test checking that all IDEA's @NotNull external annotations for JDK are reflected in Kotlin's annotations.

#KT-2778 fixed
This commit is contained in:
Evgeny Gerashchenko
2012-09-21 02:39:14 +04:00
parent 128e5580c4
commit 8c7d1b5dd0
@@ -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<PsiModifierListOwner> 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<FqName> getAffectedClasses(final VirtualFile root) {
final Set<FqName> result = Sets.newLinkedHashSet();
VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {