Add resolution for JSR 305 type qualifier nicknames

#KT-10942 In Progress
This commit is contained in:
Denis Zharkov
2017-06-06 12:45:14 +03:00
parent 79e14f88e4
commit 5c8f2a8ae8
9 changed files with 236 additions and 5 deletions
+13
View File
@@ -0,0 +1,13 @@
class A {
@javax.annotation.Nullable
String nullable() { return null; }
@javax.annotation.CheckForNull
String checkForNull() { return null; }
@javax.annotation.Nonnull
String nonNull() { return null; }
@javax.annotation.Nonnull(when = javax.annotation.meta.When.ALWAYS)
String nonNullExplicitArgument() { return null; }
}
+6
View File
@@ -0,0 +1,6 @@
class B {
@MyNullable
String myNullable() {
return null;
}
}
@@ -0,0 +1,7 @@
@java.lang.annotation.Documented
@javax.annotation.meta.TypeQualifierNickname
@javax.annotation.CheckForNull
@java.lang.annotation.Target({ElementType.METHOD, ElementType.PARAMETER})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
public @interface MyNullable {
}
@@ -651,7 +651,7 @@ public class KotlinTestUtils {
}
@NotNull
public static <M, F> List<F> createTestFiles(String testFileName, String expectedText, TestFileFactory<M, F> factory) {
public static <M, F> List<F> createTestFiles(@Nullable String testFileName, String expectedText, TestFileFactory<M, F> factory) {
return createTestFiles(testFileName, expectedText, factory, false);
}
@@ -664,6 +664,7 @@ public class KotlinTestUtils {
Matcher matcher = FILE_OR_MODULE_PATTERN.matcher(expectedText);
boolean hasModules = false;
if (!matcher.find()) {
assert testFileName != null : "testFileName should not be null if no FILE directive defined";
// One file
testFiles.add(factory.createFile(null, testFileName, expectedText, directives));
}
@@ -19,10 +19,12 @@ package org.jetbrains.kotlin.checkers
import org.jetbrains.kotlin.test.MockLibraryUtil
import java.io.File
val FOREIGN_ANNOTATIONS_SOURCES_PATH = "compiler/testData/foreignAnnotations/annotations"
abstract class AbstractForeignAnnotationsTest : AbstractDiagnosticsWithFullJdkTest() {
override fun getExtraClasspath(): List<File> =
listOf(MockLibraryUtil.compileJvmLibraryToJar(annotationsPath, "foreign-annotations"))
open protected val annotationsPath: String
get() = "compiler/testData/foreignAnnotations/annotations"
get() = FOREIGN_ANNOTATIONS_SOURCES_PATH
}
@@ -0,0 +1,141 @@
/*
* Copyright 2010-2017 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.kotlin.jvm.compiler
import org.jetbrains.kotlin.checkers.FOREIGN_ANNOTATIONS_SOURCES_PATH
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.resolveClassByFqName
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import java.io.File
class TypeQualifierAnnotationResolverTest : KtUsefulTestCase() {
companion object {
private val TEST_DATA_PATH = "compiler/testData/typeQualifierNickname/"
}
fun testBasicJSRNullabilityAnnotations() {
val (typeQualifierResolver, aClass) = buildTypeQualifierResolverAndFindClass("A")
assertMethodHasUnwrappedAnnotation(
aClass, typeQualifierResolver,
"nullable",
"@javax.annotation.Nonnull(when = When.UNKNOWN)"
)
assertMethodHasUnwrappedAnnotation(
aClass, typeQualifierResolver,
"checkForNull",
"@javax.annotation.Nonnull(when = When.MAYBE)"
)
assertMethodHasUnwrappedAnnotation(
aClass, typeQualifierResolver,
"nonNull",
"@javax.annotation.Nonnull()"
)
assertMethodHasUnwrappedAnnotation(
aClass, typeQualifierResolver,
"nonNullExplicitArgument",
"@javax.annotation.Nonnull(when = When.ALWAYS)"
)
}
fun testCustomNullabilityAnnotation() {
val (typeQualifierResolver, aClass) = buildTypeQualifierResolverAndFindClass("B")
assertMethodHasUnwrappedAnnotation(
aClass, typeQualifierResolver,
"myNullable",
"@javax.annotation.Nonnull(when = When.MAYBE)"
)
}
private fun buildTypeQualifierResolverAndFindClass(className: String): Pair<AnnotationTypeQualifierResolver, ClassDescriptor> {
val environment =
KotlinCoreEnvironment.createForTests(
myTestRootDisposable,
KotlinTestUtils.newConfiguration(
ConfigurationKind.ALL, TestJdkKind.FULL_JDK,
listOf(
KotlinTestUtils.getAnnotationsJar(),
MockLibraryUtil.compileJavaFilesLibraryToJar(
FOREIGN_ANNOTATIONS_SOURCES_PATH,
"foreign-annotations"
)
),
listOf(File(TEST_DATA_PATH))
),
EnvironmentConfigFiles.JVM_CONFIG_FILES
)
val container = JvmResolveUtil.createContainer(environment)
val typeQualifierResolver = container.get<JavaResolverComponents>().annotationTypeQualifierResolver
val aClass =
container
.get<ModuleDescriptor>()
.resolveClassByFqName(FqName(className), NoLookupLocation.FROM_TEST)!!
return typeQualifierResolver to aClass
}
private fun assertMethodHasUnwrappedAnnotation(
aClass: ClassDescriptor,
typeQualifierResolver: AnnotationTypeQualifierResolver,
methodName: String,
annotationText: String
) {
assertEquals(
annotationText,
DescriptorRenderer.withOptions {
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.ALWAYS_PARENTHESIZED
}.renderAnnotation(
aClass.findSingleTypeQualifierAnnotationOnMethod(methodName, typeQualifierResolver)
)
)
}
private fun ClassDescriptor.findSingleTypeQualifierAnnotationOnMethod(
name: String,
typeQualifierResolver: AnnotationTypeQualifierResolver
) = unsubstitutedMemberScope
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_TEST)
.single()
.annotations.single()
.let(typeQualifierResolver::resolveTypeQualifierAnnotation)
.also(::assertNotNull)!!
}