diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/IdeaJdkAnnotationsReflectedTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/IdeaJdkAnnotationsReflectedTest.java new file mode 100644 index 00000000000..b3dd8d894f1 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/IdeaJdkAnnotationsReflectedTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.jvm.compiler; + +import com.google.common.collect.Sets; +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +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.lang.resolve.java.JvmStdlibNames; +import org.jetbrains.jet.lang.resolve.lazy.KotlinTestWithEnvironment; +import org.jetbrains.jet.lang.resolve.name.FqName; + +import java.util.Set; + +public class IdeaJdkAnnotationsReflectedTest extends KotlinTestWithEnvironment { + private VirtualFile kotlinAnnotationsRoot; + private VirtualFile ideaAnnotationsRoot; + + @Override + protected JetCoreEnvironment createEnvironment() { + return JetTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea( + myTestRootDisposable, ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.FULL_JDK); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + kotlinAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("file://jdk-annotations"); + ideaAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("jar://ideaSDK/lib/jdkAnnotations.jar!/"); + } + + @Override + protected void tearDown() throws Exception { + ideaAnnotationsRoot = null; + kotlinAnnotationsRoot = null; + super.tearDown(); + } + + 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 : JdkAnnotationsSanityTest.getAffectedClasses("jar://ideaSDK/lib/jdkAnnotations.jar!/")) { + if (new FqName("org.jdom").equals(classFqName.parent())) continue; // filter unrelated jdom annotations + if (new FqName("java.util.concurrent.TransferQueue").equals(classFqName)) continue; // filter JDK7-specific class + + 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()); + } + } +} diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java index 0be08a51546..f7e4922c8fc 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/JdkAnnotationsSanityTest.java @@ -16,24 +16,23 @@ package org.jetbrains.jet.jvm.compiler; +import com.google.common.collect.Lists; 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.Disposable; +import com.intellij.openapi.util.Disposer; 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 com.intellij.testFramework.UsefulTestCase; 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.CompileEnvironmentUtil; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.di.InjectorForJavaSemanticServices; import org.jetbrains.jet.lang.descriptors.*; @@ -41,8 +40,6 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap; -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.lang.resolve.scopes.JetScope; import org.jetbrains.jet.resolve.DescriptorRenderer; @@ -54,171 +51,105 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment { - private VirtualFile kotlinAnnotationsRoot; - private VirtualFile ideaAnnotationsRoot; +public class JdkAnnotationsSanityTest extends UsefulTestCase { - @Override - protected JetCoreEnvironment createEnvironment() { + private static final int CLASSES_IN_CHUNK = 500; + + private static JetCoreEnvironment createEnvironment(Disposable parentDisposable) { return JetTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea( - myTestRootDisposable, ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.FULL_JDK); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - kotlinAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("file://jdk-annotations"); - ideaAnnotationsRoot = VirtualFileManager.getInstance().findFileByUrl("jar://ideaSDK/lib/jdkAnnotations.jar!/"); - } - - @Override - protected void tearDown() throws Exception { - ideaAnnotationsRoot = null; - kotlinAnnotationsRoot = null; - super.tearDown(); + parentDisposable, ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.FULL_JDK); } public void testNoErrorsInAlternativeSignatures() { - InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices(getProject()); + List affectedClasses = getAffectedClasses("file://jdk-annotations"); - final BindingContext bindingContext = injector.getBindingTrace().getBindingContext(); - JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + final Map> errors = Maps.newHashMap(); - final Map> errors = Maps.newHashMap(); + for (int chunkIndex = 0; chunkIndex < affectedClasses.size() / CLASSES_IN_CHUNK + 1; chunkIndex++) { + Disposable parentDisposable = CompileEnvironmentUtil.createMockDisposable(); - Iterable affectedClasses = getAffectedClasses(kotlinAnnotationsRoot); - AlternativeSignatureErrorFindingVisitor visitor = new AlternativeSignatureErrorFindingVisitor(bindingContext, errors); - for (FqName javaClass : affectedClasses) { - ClassDescriptor topLevelClass = javaDescriptorResolver.resolveClass(javaClass); - NamespaceDescriptor topLevelNamespace = javaDescriptorResolver.resolveNamespace(javaClass); - if (topLevelClass == null) { - continue; + try { + JetCoreEnvironment commonEnvironment = createEnvironment(parentDisposable); + + InjectorForJavaSemanticServices injector = new InjectorForJavaSemanticServices(commonEnvironment.getProject()); + + BindingContext bindingContext = injector.getBindingTrace().getBindingContext(); + JavaDescriptorResolver javaDescriptorResolver = injector.getJavaDescriptorResolver(); + + AlternativeSignatureErrorFindingVisitor visitor = new AlternativeSignatureErrorFindingVisitor(bindingContext, errors); + + int chunkStart = chunkIndex * CLASSES_IN_CHUNK; + for (FqName javaClass : affectedClasses.subList(chunkStart, Math.min(chunkStart + CLASSES_IN_CHUNK, affectedClasses.size()))) { + ClassDescriptor topLevelClass = javaDescriptorResolver.resolveClass(javaClass); + NamespaceDescriptor topLevelNamespace = javaDescriptorResolver.resolveNamespace(javaClass); + if (topLevelClass == null) { + continue; + } + + topLevelClass.acceptVoid(visitor); + + if (topLevelNamespace != null) { + topLevelNamespace.acceptVoid(visitor); + } + } } - - topLevelClass.acceptVoid(visitor); - - if (topLevelNamespace != null) { - topLevelNamespace.acceptVoid(visitor); + finally { + Disposer.dispose(parentDisposable); } } + if (!errors.isEmpty()) { StringBuilder sb = new StringBuilder("Error(s) in JDK alternative signatures: \n"); - for (Map.Entry> entry : errors.entrySet()) { - sb.append(DescriptorRenderer.TEXT.render(entry.getKey())).append(" : ").append(entry.getValue()).append("\n"); + for (Map.Entry> entry : errors.entrySet()) { + sb.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n"); } fail(sb.toString()); } } - private CoreExternalAnnotationsManager createFakeAnnotationsManager(VirtualFile annotationsRoot) { - CoreExternalAnnotationsManager annotationsManager = new CoreExternalAnnotationsManager(PsiManager.getInstance(getProject())); - annotationsManager.addExternalAnnotationsRoot(annotationsRoot); - return annotationsManager; - } + static List getAffectedClasses(final String rootUrl) { + Disposable myDisposable = CompileEnvironmentUtil.createMockDisposable(); - public void testAllIdeaJdkAnnotationsAreReflected() { - JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(getProject()); - GlobalSearchScope allScope = GlobalSearchScope.allScope(getProject()); + try { + createEnvironment(myDisposable); - final CoreExternalAnnotationsManager kotlinFakeAnnotationsManager = createFakeAnnotationsManager(kotlinAnnotationsRoot); - final CoreExternalAnnotationsManager ideaFakeAnnotationsManager = createFakeAnnotationsManager(ideaAnnotationsRoot); + VirtualFile root = VirtualFileManager.getInstance().findFileByUrl(rootUrl); + assert root != null; - final Set declarationsWithMissingAnnotations = Sets.newLinkedHashSet(); - - for (FqName classFqName : getAffectedClasses(ideaAnnotationsRoot)) { - if (new FqName("org.jdom").equals(classFqName.parent())) continue; // filter unrelated jdom annotations - if (new FqName("java.util.concurrent.TransferQueue").equals(classFqName)) continue; // filter JDK7-specific class - - PsiClass psiClass = javaPsiFacade.findClass(classFqName.getFqName(), allScope); - assertNotNull("Class has annotation, but it is not found: " + classFqName, psiClass); - - psiClass.accept(new JavaRecursiveElementVisitor() { + final Set result = Sets.newLinkedHashSet(); + VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() { @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 + public boolean visitFile(@NotNull VirtualFile file) { + if (ExternalAnnotationsManager.ANNOTATIONS_XML.equals(file.getName())) { + try { + String text = StreamUtil.readText(file.getInputStream()); + Matcher matcher = Pattern.compile(" getAffectedClasses(final VirtualFile root) { - final Set result = Sets.newLinkedHashSet(); - VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() { - @Override - public boolean visitFile(@NotNull VirtualFile file) { - if (ExternalAnnotationsManager.ANNOTATIONS_XML.equals(file.getName())) { - try { - String text = StreamUtil.readText(file.getInputStream()); - Matcher matcher = Pattern.compile(" { private final BindingContext bindingContext; - private final Map> errors; + private final Map> errors; - public AlternativeSignatureErrorFindingVisitor(BindingContext bindingContext, Map> errors) { + public AlternativeSignatureErrorFindingVisitor(BindingContext bindingContext, Map> errors) { this.bindingContext = bindingContext; this.errors = errors; } @@ -251,7 +182,7 @@ public class JdkAnnotationsSanityTest extends KotlinTestWithEnvironment { private Void visitDeclaration(@NotNull DeclarationDescriptor descriptor) { List errors = bindingContext.get(BindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor); if (errors != null) { - this.errors.put(descriptor, errors); + this.errors.put(DescriptorRenderer.TEXT.render(descriptor), errors); } return null; }