Fix collecting source retention annotations

#KT-12187 fixed
This commit is contained in:
Alexey Tsvetkov
2016-05-05 20:05:07 +03:00
parent cac49b6609
commit 10b9be9f51
30 changed files with 268 additions and 30 deletions
@@ -279,7 +279,7 @@ public abstract class AnnotationCodegen {
ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor(); ClassifierDescriptor classifierDescriptor = annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
assert classifierDescriptor != null : "Annotation descriptor has no class: " + annotationDescriptor; assert classifierDescriptor != null : "Annotation descriptor has no class: " + annotationDescriptor;
RetentionPolicy rp = getRetentionPolicy(classifierDescriptor); RetentionPolicy rp = getRetentionPolicy(classifierDescriptor);
if (rp == RetentionPolicy.SOURCE && typeMapper.getClassBuilderMode() != ClassBuilderMode.LIGHT_CLASSES) { if (rp == RetentionPolicy.SOURCE && typeMapper.getClassBuilderMode() == ClassBuilderMode.FULL) {
return null; return null;
} }
@@ -70,7 +70,10 @@ public class CodegenTestUtil {
/* useTypeTableInSerializer = */ false, /* useTypeTableInSerializer = */ false,
configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false) configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
); );
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
if (analysisResult.getShouldGenerateCode()) {
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
}
// For JVM-specific errors // For JVM-specific errors
AnalyzingUtils.throwExceptionOnErrors(state.getCollectedExtraJvmDiagnostics()); AnalyzingUtils.throwExceptionOnErrors(state.getCollectedExtraJvmDiagnostics());
@@ -1,8 +0,0 @@
package example
import java.lang.annotation.ElementType
import java.lang.annotation.Inherited
import java.lang.annotation.Target
@Inherited
annotation public class ExampleAnnotation
@@ -6,19 +6,31 @@ import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion import javax.lang.model.SourceVersion
import javax.lang.model.element.ElementKind import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic import kotlin.reflect.KClass
public class ExampleAnnotationProcessor : AbstractProcessor() { class ExampleAnnotationProcessor : AbstractProcessor() {
private companion object { private companion object {
val ANNOTATION_FQ_NAME = ExampleAnnotation::class.java.canonicalName val ANNOTATION_TO_PREFIX = mapOf(ExampleAnnotation::class to "",
ExampleSourceAnnotation::class to "SourceAnnotated",
ExampleRuntimeAnnotation::class to "RuntimeAnnotated",
ExampleBinaryAnnotation::class to "BinaryAnnotated")
val SUFFIX_OPTION = "suffix" val SUFFIX_OPTION = "suffix"
val GENERATE_KOTLIN_CODE_OPTION = "generate.kotlin.code" val GENERATE_KOTLIN_CODE_OPTION = "generate.kotlin.code"
val KAPT_KOTLIN_GENERATED_OPTION = "kapt.kotlin.generated" val KAPT_KOTLIN_GENERATED_OPTION = "kapt.kotlin.generated"
} }
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean { override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean {
val elements = roundEnv.getElementsAnnotatedWith(ExampleAnnotation::class.java) for ((annotation, prefix) in ANNOTATION_TO_PREFIX) {
processAnnotation(roundEnv, annotation, prefix)
}
return true
}
private fun <T : Annotation> processAnnotation(roundEnv: RoundEnvironment, annotationClass: KClass<T>, generatedFilePrefix: String) {
val elements = roundEnv.getElementsAnnotatedWith(annotationClass.java)
val elementUtils = processingEnv.elementUtils val elementUtils = processingEnv.elementUtils
val filer = processingEnv.filer val filer = processingEnv.filer
@@ -30,28 +42,26 @@ public class ExampleAnnotationProcessor : AbstractProcessor() {
for (element in elements) { for (element in elements) {
val packageName = elementUtils.getPackageOf(element).qualifiedName.toString() val packageName = elementUtils.getPackageOf(element).qualifiedName.toString()
val simpleName = element.simpleName val simpleName = element.simpleName.toString()
val className = simpleName.toString().capitalize() + generatedFileSuffix val generatedJavaClassName = generatedFilePrefix.capitalize() + simpleName.capitalize() + generatedFileSuffix
filer.createSourceFile(className).openWriter().use { with(it) { filer.createSourceFile(generatedJavaClassName).openWriter().use { with(it) {
appendln("package $packageName;") appendln("package $packageName;")
appendln("public final class $className {}") appendln("public final class $generatedJavaClassName {}")
}} }}
if (generateKotlinCode && kotlinGenerated != null && element.kind == ElementKind.CLASS) { if (generateKotlinCode && kotlinGenerated != null && element.kind == ElementKind.CLASS) {
File(kotlinGenerated, "$simpleName.kt").writer().buffered().use { File(kotlinGenerated, "$simpleName.kt").writer().buffered().use {
it.appendln("package $packageName") it.appendln("package $packageName")
it.appendln("fun $simpleName.customToString() = \"$simpleName: \" + toString()") it.appendln("fun $simpleName.customToString() = \"$generatedJavaClassName: \" + toString()")
} }
} }
} }
return true;
} }
override fun getSupportedSourceVersion() = SourceVersion.RELEASE_6 override fun getSupportedSourceVersion() = SourceVersion.RELEASE_6
override fun getSupportedAnnotationTypes() = setOf(ANNOTATION_FQ_NAME) override fun getSupportedAnnotationTypes() = ANNOTATION_TO_PREFIX.keys.map { it.java.canonicalName }.toSet()
override fun getSupportedOptions() = setOf(SUFFIX_OPTION) override fun getSupportedOptions() = setOf(SUFFIX_OPTION)
} }
@@ -0,0 +1,18 @@
package example
import java.lang.annotation.Inherited
@Inherited
annotation class ExampleAnnotation
@Inherited
@Retention(AnnotationRetention.SOURCE)
annotation class ExampleSourceAnnotation
@Inherited
@Retention(AnnotationRetention.BINARY)
annotation class ExampleBinaryAnnotation
@Inherited
@Retention(AnnotationRetention.RUNTIME)
annotation class ExampleRuntimeAnnotation
@@ -26,6 +26,9 @@ class KaptIT: BaseGradleIT() {
assertFileExists("build/generated/source/kapt/main/TestClassGenerated.java") assertFileExists("build/generated/source/kapt/main/TestClassGenerated.java")
assertFileExists("build/classes/main/example/TestClass.class") assertFileExists("build/classes/main/example/TestClass.class")
assertFileExists("build/classes/main/example/TestClassGenerated.class") assertFileExists("build/classes/main/example/TestClassGenerated.class")
assertNoSuchFile("build/classes/main/example/SourceAnnotatedTestClassGenerated.class")
assertFileExists("build/classes/main/example/BinaryAnnotatedTestClassGenerated.class")
assertFileExists("build/classes/main/example/RuntimeAnnotatedTestClassGenerated.class")
} }
project.build("build") { project.build("build") {
@@ -46,6 +49,9 @@ class KaptIT: BaseGradleIT() {
assertFileExists("build/generated/source/kapt/main/TestClassGenerated.java") assertFileExists("build/generated/source/kapt/main/TestClassGenerated.java")
assertFileExists("build/classes/main/example/TestClass.class") assertFileExists("build/classes/main/example/TestClass.class")
assertFileExists("build/classes/main/example/TestClassGenerated.class") assertFileExists("build/classes/main/example/TestClassGenerated.class")
assertFileExists("build/classes/main/example/SourceAnnotatedTestClassGenerated.class")
assertFileExists("build/classes/main/example/BinaryAnnotatedTestClassGenerated.class")
assertFileExists("build/classes/main/example/RuntimeAnnotatedTestClassGenerated.class")
} }
project.build("build") { project.build("build") {
@@ -20,6 +20,8 @@ public class JavaClass {
public void test() { public void test() {
System.out.println(example.TestClassGenerated.class.getName()); System.out.println(example.TestClassGenerated.class.getName());
System.out.println(example.BinaryAnnotatedTestClassGenerated.class.getName());
System.out.println(example.RuntimeAnnotatedTestClassGenerated.class.getName());
} }
} }
@@ -1,6 +1,9 @@
package example package example
@example.ExampleAnnotation @example.ExampleAnnotation
@example.ExampleSourceAnnotation
@example.ExampleBinaryAnnotation
@example.ExampleRuntimeAnnotation
public class TestClass { public class TestClass {
@example.ExampleAnnotation @example.ExampleAnnotation
@@ -20,6 +20,9 @@ public class JavaClass {
public void test() { public void test() {
System.out.println(example.TestClassGenerated.class.getName()); System.out.println(example.TestClassGenerated.class.getName());
System.out.println(example.SourceAnnotatedTestClassGenerated.class.getName());
System.out.println(example.BinaryAnnotatedTestClassGenerated.class.getName());
System.out.println(example.RuntimeAnnotatedTestClassGenerated.class.getName());
} }
} }
@@ -1,6 +1,9 @@
package example package example
@example.ExampleAnnotation @example.ExampleAnnotation
@example.ExampleSourceAnnotation
@example.ExampleBinaryAnnotation
@example.ExampleRuntimeAnnotation
public class TestClass { public class TestClass {
@example.ExampleAnnotation @example.ExampleAnnotation
@@ -0,0 +1,4 @@
import java.lang.annotation.*;
@Retention(RetentionPolicy.CLASS)
public @interface ClassAnnotation {}
@@ -0,0 +1,3 @@
import java.lang.annotation.*;
public @interface DefaultAnnotation {}
@@ -0,0 +1,4 @@
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {}
@@ -0,0 +1,4 @@
import java.lang.annotation.*;
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {}
@@ -0,0 +1,6 @@
a DefaultAnnotation 0
c 0 DafaultUsage
a ClassAnnotation 1
c 1 ClassUsage
a RuntimeAnnotation 2
c 2 RuntimeUsage
@@ -0,0 +1,11 @@
@DefaultAnnotation
class DafaultUsage
@SourceAnnotation
class SourceUsage
@ClassAnnotation
class ClassUsage
@RuntimeAnnotation
class RuntimeUsage
@@ -0,0 +1,4 @@
import java.lang.annotation.*;
@Retention(RetentionPolicy.CLASS)
public @interface ClassAnnotation {}
@@ -0,0 +1,3 @@
import java.lang.annotation.*;
public @interface DefaultAnnotation {}
@@ -0,0 +1,4 @@
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {}
@@ -0,0 +1,4 @@
import java.lang.annotation.*;
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {}
@@ -0,0 +1,8 @@
a DefaultAnnotation 0
c 0 DafaultUsage
a SourceAnnotation 1
c 1 SourceUsage
a ClassAnnotation 2
c 2 ClassUsage
a RuntimeAnnotation 3
c 3 RuntimeUsage
@@ -0,0 +1,11 @@
@DefaultAnnotation
class DafaultUsage
@SourceAnnotation
class SourceUsage
@ClassAnnotation
class ClassUsage
@RuntimeAnnotation
class RuntimeUsage
@@ -0,0 +1,10 @@
annotation class DefaultAnnotation
@Retention(AnnotationRetention.SOURCE)
annotation class SourceAnnotation
@Retention(AnnotationRetention.BINARY)
annotation class BinaryAnnotation
@Retention(AnnotationRetention.RUNTIME)
annotation class RuntimeAnnotation
@@ -0,0 +1,15 @@
a java.lang.annotation.Retention 0
c 0 DefaultAnnotation
a kotlin.annotation.Retention 1
c 1 SourceAnnotation
c 0 SourceAnnotation
c 1 BinaryAnnotation
c 0 BinaryAnnotation
c 1 RuntimeAnnotation
c 0 RuntimeAnnotation
a DefaultAnnotation 2
c 2 DafaultUsage
a BinaryAnnotation 3
c 3 BinaryUsage
a RuntimeAnnotation 4
c 4 RuntimeUsage
@@ -0,0 +1,11 @@
@DefaultAnnotation
class DafaultUsage
@SourceAnnotation
class SourceUsage
@BinaryAnnotation
class BinaryUsage
@RuntimeAnnotation
class RuntimeUsage
@@ -0,0 +1,10 @@
annotation class DefaultAnnotation
@Retention(AnnotationRetention.SOURCE)
annotation class SourceAnnotation
@Retention(AnnotationRetention.BINARY)
annotation class BinaryAnnotation
@Retention(AnnotationRetention.RUNTIME)
annotation class RuntimeAnnotation
@@ -0,0 +1,17 @@
a java.lang.annotation.Retention 0
c 0 DefaultAnnotation
a kotlin.annotation.Retention 1
c 1 SourceAnnotation
c 0 SourceAnnotation
c 1 BinaryAnnotation
c 0 BinaryAnnotation
c 1 RuntimeAnnotation
c 0 RuntimeAnnotation
a DefaultAnnotation 2
c 2 DafaultUsage
a SourceAnnotation 3
c 3 SourceUsage
a BinaryAnnotation 4
c 4 BinaryUsage
a RuntimeAnnotation 5
c 5 RuntimeUsage
@@ -0,0 +1,11 @@
@DefaultAnnotation
class DafaultUsage
@SourceAnnotation
class SourceUsage
@BinaryAnnotation
class BinaryUsage
@RuntimeAnnotation
class RuntimeUsage
@@ -16,12 +16,14 @@
package org.jetbrains.kotlin.annotation package org.jetbrains.kotlin.annotation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.CodegenTestCase import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.codegen.CodegenTestUtil import org.jetbrains.kotlin.codegen.CodegenTestUtil
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.test.TestJdkKind
@@ -29,13 +31,26 @@ import java.io.File
import java.io.StringWriter import java.io.StringWriter
abstract class AbstractAnnotationProcessorBoxTest : CodegenTestCase() { abstract class AbstractAnnotationProcessorBoxTest : CodegenTestCase() {
override fun doTest(path: String) { override fun doTest(path: String) {
val testName = getTestName(true) val testDir = File(path)
val ktFiles = File(path).listFiles { file -> file.isFile && file.extension.toLowerCase() == "kt" }
val testFiles = ktFiles.map { TestFile(it.name, it.readText()) }
val supportInheritedAnnotations = testName.startsWith("inherited")
val collectorExtension = createTestEnvironment(supportInheritedAnnotations) fun filesByExtension(ext: String) = testDir.listFiles { file -> file.isFile && file.extension.equals(ext, ignoreCase = true) }
val testName = getTestName(true)
val ktFiles = filesByExtension("kt")
val testFiles = ktFiles.map { TestFile(it.name, it.readText()) }
val supportInheritedAnnotations = testName.contains("inherited", ignoreCase = true)
val supportStubs = testName.contains("stubs", ignoreCase = true)
val javaSourceRoots = mutableListOf<File>()
val javaFiles = filesByExtension("java")
if (javaFiles.isNotEmpty()) {
val javaFilesDir = KotlinTestUtils.tmpDir("java-files")
javaFiles.forEach { it.copyTo(File(javaFilesDir, it.name)) }
javaSourceRoots.add(javaFilesDir)
}
val collectorExtension = createTestEnvironment(supportInheritedAnnotations, supportStubs, javaSourceRoots)
loadMultiFiles(testFiles) loadMultiFiles(testFiles)
CodegenTestUtil.generateFiles(myEnvironment, myFiles) CodegenTestUtil.generateFiles(myEnvironment, myFiles)
@@ -49,14 +64,27 @@ abstract class AbstractAnnotationProcessorBoxTest : CodegenTestCase() {
return "plugins/annotation-collector/testData/codegen/" return "plugins/annotation-collector/testData/codegen/"
} }
private fun createTestEnvironment(supportInheritedAnnotations: Boolean): AnnotationCollectorExtensionForTests { private fun createTestEnvironment(
val configuration = KotlinTestUtils.compilerConfigurationForTests(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK) supportInheritedAnnotations: Boolean,
supportStubs: Boolean,
javaSourceRoots: List<File>
): AnnotationCollectorExtensionForTests {
val configuration = KotlinTestUtils.compilerConfigurationForTests(ConfigurationKind.ALL,
TestJdkKind.MOCK_JDK,
/* classpath = */ emptyList(),
javaSourceRoots)
val environment = KotlinCoreEnvironment.createForTests(testRootDisposable!!, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForTests(testRootDisposable!!, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val project = environment.project val project = environment.project
val collectorExtension = AnnotationCollectorExtensionForTests(supportInheritedAnnotations) val collectorExtension = AnnotationCollectorExtensionForTests(supportInheritedAnnotations)
ClassBuilderInterceptorExtension.registerExtension(project, collectorExtension) ClassBuilderInterceptorExtension.registerExtension(project, collectorExtension)
if (supportStubs) {
val stubsDir = KotlinTestUtils.tmpDir("class-stubs")
val stubProducerExtension = StubProducerExtension(stubsDir, MessageCollector.NONE)
AnalysisCompletedHandlerExtension.registerExtension(project, stubProducerExtension)
}
myEnvironment = environment myEnvironment = environment
return collectorExtension return collectorExtension
@@ -83,6 +83,12 @@ public class AnnotationProcessorBoxTestGenerated extends AbstractAnnotationProce
doTest(fileName); doTest(fileName);
} }
@TestMetadata("inheritedSimple")
public void testInheritedSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/inheritedSimple/");
doTest(fileName);
}
@TestMetadata("inheritedTopLevel") @TestMetadata("inheritedTopLevel")
public void testInheritedTopLevel() throws Exception { public void testInheritedTopLevel() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/inheritedTopLevel/"); String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/inheritedTopLevel/");
@@ -125,6 +131,30 @@ public class AnnotationProcessorBoxTestGenerated extends AbstractAnnotationProce
doTest(fileName); doTest(fileName);
} }
@TestMetadata("retentionPoliciesJavaAnnotations")
public void testRetentionPoliciesJavaAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/retentionPoliciesJavaAnnotations/");
doTest(fileName);
}
@TestMetadata("retentionPoliciesJavaAnnotationsStubs")
public void testRetentionPoliciesJavaAnnotationsStubs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/retentionPoliciesJavaAnnotationsStubs/");
doTest(fileName);
}
@TestMetadata("retentionPoliciesKotlinAnnotations")
public void testRetentionPoliciesKotlinAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/retentionPoliciesKotlinAnnotations/");
doTest(fileName);
}
@TestMetadata("retentionPoliciesKotlinAnnotationsStubs")
public void testRetentionPoliciesKotlinAnnotationsStubs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/retentionPoliciesKotlinAnnotationsStubs/");
doTest(fileName);
}
@TestMetadata("topLevelAnnotated") @TestMetadata("topLevelAnnotated")
public void testTopLevelAnnotated() throws Exception { public void testTopLevelAnnotated() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/topLevelAnnotated/"); String fileName = KotlinTestUtils.navigationMetadata("plugins/annotation-collector/testData/collectToFile/topLevelAnnotated/");