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:
@@ -18,22 +18,29 @@ package org.jetbrains.jet.jvm.compiler;
|
|||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.intellij.codeInsight.AnnotationUtil;
|
||||||
import com.intellij.codeInsight.ExternalAnnotationsManager;
|
import com.intellij.codeInsight.ExternalAnnotationsManager;
|
||||||
import com.intellij.openapi.util.io.StreamUtil;
|
import com.intellij.openapi.util.io.StreamUtil;
|
||||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||||
import com.intellij.openapi.vfs.VirtualFileVisitor;
|
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.annotations.NotNull;
|
||||||
import org.jetbrains.jet.ConfigurationKind;
|
import org.jetbrains.jet.ConfigurationKind;
|
||||||
import org.jetbrains.jet.JetTestUtils;
|
import org.jetbrains.jet.JetTestUtils;
|
||||||
import org.jetbrains.jet.TestJdkKind;
|
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.cli.jvm.compiler.JetCoreEnvironment;
|
||||||
import org.jetbrains.jet.di.InjectorForJavaSemanticServices;
|
import org.jetbrains.jet.di.InjectorForJavaSemanticServices;
|
||||||
import org.jetbrains.jet.lang.BuiltinsScopeExtensionMode;
|
import org.jetbrains.jet.lang.BuiltinsScopeExtensionMode;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
|
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.lazy.KotlinTestWithEnvironment;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.resolve.DescriptorRenderer;
|
import org.jetbrains.jet.resolve.DescriptorRenderer;
|
||||||
@@ -46,6 +53,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment {
|
public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment {
|
||||||
private VirtualFile kotlinAnnotationsRoot;
|
private VirtualFile kotlinAnnotationsRoot;
|
||||||
|
private VirtualFile ideaAnnotationsRoot;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JetCoreEnvironment createEnvironment() {
|
protected JetCoreEnvironment createEnvironment() {
|
||||||
@@ -57,6 +65,7 @@ public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment {
|
|||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
kotlinAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("file://jdk-annotations");
|
kotlinAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("file://jdk-annotations");
|
||||||
|
ideaAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("jar://ideaSDK/lib/jdkAnnotations.jar!/");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNoErrorsInAlternativeSignatures() {
|
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) {
|
private static Iterable<FqName> getAffectedClasses(final VirtualFile root) {
|
||||||
final Set<FqName> result = Sets.newLinkedHashSet();
|
final Set<FqName> result = Sets.newLinkedHashSet();
|
||||||
VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
|
VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
|
||||||
|
|||||||
Reference in New Issue
Block a user