diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaAnnotationImpl.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaAnnotationImpl.java index 6b6bc8b919e..49598467704 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaAnnotationImpl.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaAnnotationImpl.java @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.load.java.structure.impl; +import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; @@ -86,4 +87,11 @@ public class JavaAnnotationImpl extends JavaElementImpl implement PsiElement resolved = referenceElement.resolve(); return resolved instanceof PsiClass ? (PsiClass) resolved : null; } + + @Override + public boolean isIdeExternalAnnotation() { + PsiAnnotation psi = getPsi(); + ExternalAnnotationsManager externalAnnotationManager = ExternalAnnotationsManager.getInstance(psi.getProject()); + return externalAnnotationManager.isExternalAnnotation(psi); + } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementCollectionFromPsiArrayUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementCollectionFromPsiArrayUtil.kt index 92cfc8fcdea..e793e0e6a01 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementCollectionFromPsiArrayUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementCollectionFromPsiArrayUtil.kt @@ -20,6 +20,7 @@ package org.jetbrains.kotlin.load.java.structure.impl import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.load.java.NULLABILITY_ANNOTATIONS import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.name.Name @@ -67,6 +68,14 @@ internal fun classifierTypes(classTypes: Array): Collection): Collection = annotations.convert(::JavaAnnotationImpl) +internal fun nullabilityAnnotations(annotations: Array): Collection = + annotations.convert(::JavaAnnotationImpl) + .filter { annotation -> + val fqName = annotation.classId?.asSingleFqName() ?: return@filter false + fqName in NULLABILITY_ANNOTATIONS + } + + internal fun namedAnnotationArguments(nameValuePairs: Array): Collection = nameValuePairs.convert { psi -> val name = psi.name?.let(Name::identifier) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementUtil.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementUtil.java index c0a225744c3..89dd96b37c1 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementUtil.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaElementUtil.java @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.load.java.structure.impl; +import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,10 +26,12 @@ import org.jetbrains.kotlin.load.java.JavaVisibilities; import org.jetbrains.kotlin.load.java.structure.JavaAnnotation; import org.jetbrains.kotlin.name.FqName; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import static org.jetbrains.kotlin.load.java.structure.impl.JavaElementCollectionFromPsiArrayUtil.annotations; +import static org.jetbrains.kotlin.load.java.structure.impl.JavaElementCollectionFromPsiArrayUtil.nullabilityAnnotations; /* package */ class JavaElementUtil { private JavaElementUtil() { @@ -70,6 +73,27 @@ import static org.jetbrains.kotlin.load.java.structure.impl.JavaElementCollectio return Collections.emptyList(); } + @Nullable + private static PsiAnnotation[] getExternalAnnotations(@NotNull JavaModifierListOwnerImpl modifierListOwner) { + PsiModifierListOwner psiModifierListOwner = modifierListOwner.getPsi(); + ExternalAnnotationsManager externalAnnotationManager = ExternalAnnotationsManager + .getInstance(psiModifierListOwner.getProject()); + return externalAnnotationManager.findExternalAnnotations(psiModifierListOwner); + } + + @NotNull + static + Collection getRegularAndExternalAnnotations(@NotNull T owner) { + PsiAnnotation[] externalAnnotations = getExternalAnnotations(owner); + if (externalAnnotations == null) { + return getAnnotations(owner); + } + Collection annotations = new ArrayList<>(getAnnotations(owner)); + annotations.addAll(nullabilityAnnotations(externalAnnotations)); + return annotations; + } + + @Nullable public static JavaAnnotation findAnnotation(@NotNull JavaAnnotationOwnerImpl owner, @NotNull FqName fqName) { PsiAnnotationOwner annotationOwnerPsi = owner.getAnnotationOwnerPsi(); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaMemberImpl.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaMemberImpl.java index 2d057e4797e..250485886a1 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaMemberImpl.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaMemberImpl.java @@ -83,7 +83,7 @@ public abstract class JavaMemberImpl extends JavaElementI @NotNull @Override public Collection getAnnotations() { - return JavaElementUtil.getAnnotations(this); + return JavaElementUtil.getRegularAndExternalAnnotations(this); } @Nullable diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaValueParameterImpl.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaValueParameterImpl.java index ab54fec64bc..e83421b403f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaValueParameterImpl.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaValueParameterImpl.java @@ -67,7 +67,7 @@ public class JavaValueParameterImpl extends JavaElementImpl @NotNull @Override public Collection getAnnotations() { - return JavaElementUtil.getAnnotations(this); + return JavaElementUtil.getRegularAndExternalAnnotations(this); } @Nullable diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt index 261e5fcf8a9..a998cb949b0 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/JavaAnnotationMapper.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.load.java.descriptors.PossiblyExternalAnnotationDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaAnnotationDescriptor import org.jetbrains.kotlin.load.java.structure.* @@ -103,7 +104,7 @@ open class JavaAnnotationDescriptor( c: LazyJavaResolverContext, annotation: JavaAnnotation?, override val fqName: FqName -) : AnnotationDescriptor { +) : AnnotationDescriptor, PossiblyExternalAnnotationDescriptor { override val source: SourceElement = annotation?.let { c.components.sourceElementFactory.source(it) } ?: SourceElement.NO_SOURCE override val type: SimpleType by c.storageManager.createLazyValue { c.module.builtIns.getBuiltInClassByFqName(fqName).defaultType } @@ -111,6 +112,8 @@ open class JavaAnnotationDescriptor( protected val firstArgument: JavaAnnotationArgument? = annotation?.arguments?.firstOrNull() override val allValueArguments: Map> get() = emptyMap() + + override val isIdeExternalAnnotation: Boolean = annotation?.isIdeExternalAnnotation == true } class JavaDeprecatedAnnotationDescriptor( diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/descriptors/PossiblyExternalAnnotationDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/descriptors/PossiblyExternalAnnotationDescriptor.kt new file mode 100644 index 00000000000..36a1ae75139 --- /dev/null +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/descriptors/PossiblyExternalAnnotationDescriptor.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.load.java.descriptors + +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor + +interface PossiblyExternalAnnotationDescriptor : AnnotationDescriptor { + val isIdeExternalAnnotation: Boolean +} \ No newline at end of file diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt index 98c3f933d71..272d9258760 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaAnnotationDescriptor.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies import org.jetbrains.kotlin.load.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.load.java.descriptors.PossiblyExternalAnnotationDescriptor import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext import org.jetbrains.kotlin.load.java.lazy.types.toAttributes import org.jetbrains.kotlin.load.java.structure.* @@ -39,7 +40,7 @@ import org.jetbrains.kotlin.types.isError class LazyJavaAnnotationDescriptor( private val c: LazyJavaResolverContext, private val javaAnnotation: JavaAnnotation -) : AnnotationDescriptor { +) : AnnotationDescriptor, PossiblyExternalAnnotationDescriptor { override val fqName by c.storageManager.createNullableLazyValue { javaAnnotation.classId?.asSingleFqName() } @@ -112,4 +113,6 @@ class LazyJavaAnnotationDescriptor( ClassId.topLevel(fqName), c.components.deserializedDescriptorResolver.components.notFoundClasses ) + + override val isIdeExternalAnnotation: Boolean = javaAnnotation.isIdeExternalAnnotation } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt index 907cdb44995..013bbf4f060 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt @@ -48,6 +48,8 @@ interface JavaTypeParameterListOwner : JavaElement { interface JavaAnnotation : JavaElement { val arguments: Collection val classId: ClassId? + val isIdeExternalAnnotation: Boolean + get() = false fun resolve(): JavaClass? } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt index 2ad545b364e..25644e71322 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.load.java.typeEnhancement import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotated -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations +import org.jetbrains.kotlin.descriptors.annotations.* import org.jetbrains.kotlin.load.java.* import org.jetbrains.kotlin.load.java.descriptors.* import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext @@ -34,6 +31,7 @@ import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.deprecation.DEPRECATED_FUNCTION_KEY import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull +import org.jetbrains.kotlin.resolve.descriptorUtil.isSourceAnnotation import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.isTypeParameter @@ -103,6 +101,12 @@ class SignatureEnhancement( isForWarningOnly = true ) else -> null + }?.let { migrationStatus -> + if (!migrationStatus.isForWarningOnly + && annotationDescriptor is PossiblyExternalAnnotationDescriptor + && annotationDescriptor.isIdeExternalAnnotation) + migrationStatus.copy(isForWarningOnly = true) + else migrationStatus } } diff --git a/idea/testData/externalAnnotations/ClassWithExternalAnnotatedMembers.java b/idea/testData/externalAnnotations/ClassWithExternalAnnotatedMembers.java new file mode 100644 index 00000000000..c440971993c --- /dev/null +++ b/idea/testData/externalAnnotations/ClassWithExternalAnnotatedMembers.java @@ -0,0 +1,14 @@ +public class ClassWithExternalAnnotatedMembers { + String nullableMethod() { + return null; + } + + String notNullMethod() { + return "nya"; + } + + void methodWithNotNullParameter(Integer x) { + } + + String nullableField = "nya"; +} diff --git a/idea/testData/externalAnnotations/annotations/annotations.xml b/idea/testData/externalAnnotations/annotations/annotations.xml new file mode 100644 index 00000000000..18cc21c99f3 --- /dev/null +++ b/idea/testData/externalAnnotations/annotations/annotations.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/externalAnnotations/notNullMethod.kt b/idea/testData/externalAnnotations/notNullMethod.kt new file mode 100644 index 00000000000..b23dd3743b9 --- /dev/null +++ b/idea/testData/externalAnnotations/notNullMethod.kt @@ -0,0 +1,4 @@ +fun test() { + val x = ClassWithExternalAnnotatedMembers() + x.notNullMethod()?.toLowerCase() +} \ No newline at end of file diff --git a/idea/testData/externalAnnotations/nullableField.kt b/idea/testData/externalAnnotations/nullableField.kt new file mode 100644 index 00000000000..61d53ce3834 --- /dev/null +++ b/idea/testData/externalAnnotations/nullableField.kt @@ -0,0 +1,5 @@ +@Suppress("UNUSED_VARIABLE") +fun test() { + val x = ClassWithExternalAnnotatedMembers() + val y: String = x.nullableField +} \ No newline at end of file diff --git a/idea/testData/externalAnnotations/nullableMethod.kt b/idea/testData/externalAnnotations/nullableMethod.kt new file mode 100644 index 00000000000..8dca9cca89f --- /dev/null +++ b/idea/testData/externalAnnotations/nullableMethod.kt @@ -0,0 +1,5 @@ +@Suppress("UNUSED_VARIABLE") +fun test() { + val x = ClassWithExternalAnnotatedMembers() + val y: String = x.nullableMethod() +} \ No newline at end of file diff --git a/idea/testData/externalAnnotations/nullableMethodParameter.kt b/idea/testData/externalAnnotations/nullableMethodParameter.kt new file mode 100644 index 00000000000..6dc987704d2 --- /dev/null +++ b/idea/testData/externalAnnotations/nullableMethodParameter.kt @@ -0,0 +1,4 @@ +fun test() { + val x = ClassWithExternalAnnotatedMembers() + x.methodWithNotNullParameter(null) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/externalAnnotations/ExternalAnnotationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/externalAnnotations/ExternalAnnotationTest.kt new file mode 100644 index 00000000000..d7ae7873355 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/externalAnnotations/ExternalAnnotationTest.kt @@ -0,0 +1,74 @@ +package org.jetbrains.kotlin.idea.externalAnnotations + +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.JavaModuleExternalPaths +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.psi.codeStyle.JavaCodeStyleSettings +import com.intellij.testFramework.LightPlatformTestCase +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TargetBackend +import java.io.File + +class ExternalAnnotationTest : KotlinLightCodeInsightFixtureTestCase() { + + fun testNotNullMethod() { + KotlinTestUtils.runTest(::doTest, TargetBackend.ANY, "idea/testData/externalAnnotations/notNullMethod.kt") + } + + fun testNullableMethod() { + KotlinTestUtils.runTest(::doTest, TargetBackend.ANY, "idea/testData/externalAnnotations/nullableMethod.kt") + } + + + fun testNullableField() { + KotlinTestUtils.runTest(::doTest, TargetBackend.ANY, "idea/testData/externalAnnotations/nullableField.kt") + } + + fun testNullableMethodParameter() { + KotlinTestUtils.runTest(::doTest, TargetBackend.ANY, "idea/testData/externalAnnotations/nullableMethodParameter.kt") + } + + override fun setUp() { + super.setUp() + JavaCodeStyleSettings.getInstance(project).USE_EXTERNAL_ANNOTATIONS = true + addFile(classWithExternalAnnotatedMembers) + } + + override fun tearDown() { + JavaCodeStyleSettings.getInstance(project).USE_EXTERNAL_ANNOTATIONS = false + super.tearDown() + } + + private fun addFile(path: String) { + val file = File(path) + val root = LightPlatformTestCase.getSourceRoot() + runWriteAction { + val virtualFile = root.createChildData(null, file.name) + virtualFile.getOutputStream(null).writer().use { it.write(FileUtil.loadFile(file)) } + } + } + + private fun doTest(kotlinFilePath: String) { + myFixture.configureByFiles(kotlinFilePath, externalAnnotationsFile, classWithExternalAnnotatedMembers) + myFixture.checkHighlighting() + } + + override fun getProjectDescriptor() = object : KotlinWithJdkAndRuntimeLightProjectDescriptor() { + override fun configureModule(module: Module, model: ModifiableRootModel) { + super.configureModule(module, model) + model.getModuleExtension(JavaModuleExternalPaths::class.java) + .setExternalAnnotationUrls(arrayOf(VfsUtilCore.pathToUrl(externalAnnotationsPath))) + } + } + + companion object { + private const val externalAnnotationsPath = "idea/testData/externalAnnotations/annotations/" + private const val classWithExternalAnnotatedMembers = "idea/testData/externalAnnotations/ClassWithExternalAnnotatedMembers.java" + private const val externalAnnotationsFile = "$externalAnnotationsPath/annotations.xml" + } +}