Kapt: support multiple annotation processing steps (KT-13651)
(cherry picked from commit 880e183)
This commit is contained in:
committed by
Yan Zhulanow
parent
6ceaac63dc
commit
824b778a7b
@@ -143,7 +143,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
configs.map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) }
|
configs.map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) }
|
||||||
}
|
}
|
||||||
else null
|
else null
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
})
|
})
|
||||||
|
|
||||||
KotlinScriptExternalImportsProvider.getInstance(project)?.run {
|
KotlinScriptExternalImportsProvider.getInstance(project)?.run {
|
||||||
@@ -162,7 +162,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
updateClasspathFromRootsIndex(rootsIndex)
|
updateClasspathFromRootsIndex(rootsIndex)
|
||||||
|
|
||||||
(ServiceManager.getService(project, CoreJavaFileManager::class.java)
|
(ServiceManager.getService(project, CoreJavaFileManager::class.java)
|
||||||
as KotlinCliJavaFileManagerImpl).initIndex(rootsIndex)
|
as KotlinCliJavaFileManagerImpl).initIndex(rootsIndex)
|
||||||
|
|
||||||
project.registerService(JvmVirtualFileFinderFactory::class.java, JvmCliVirtualFileFinderFactory(rootsIndex))
|
project.registerService(JvmVirtualFileFinderFactory::class.java, JvmCliVirtualFileFinderFactory(rootsIndex))
|
||||||
|
|
||||||
@@ -196,8 +196,8 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Iterable<ContentRoot>.classpathRoots(): List<JavaRoot> =
|
private fun Iterable<ContentRoot>.classpathRoots(): List<JavaRoot> =
|
||||||
filterIsInstance(JvmContentRoot::class.java).mapNotNull {
|
filterIsInstance(JvmContentRoot::class.java).mapNotNull {
|
||||||
javaRoot -> contentRootToVirtualFile(javaRoot)?.let { virtualFile ->
|
javaRoot -> contentRootToVirtualFile(javaRoot)?.let { virtualFile ->
|
||||||
|
|
||||||
val prefixPackageFqName = (javaRoot as? JavaSourceRoot)?.packagePrefix?.let {
|
val prefixPackageFqName = (javaRoot as? JavaSourceRoot)?.packagePrefix?.let {
|
||||||
if (isValidJavaFqName(it)) {
|
if (isValidJavaFqName(it)) {
|
||||||
@@ -217,7 +217,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
|
|
||||||
JavaRoot(virtualFile, rootType, prefixPackageFqName)
|
JavaRoot(virtualFile, rootType, prefixPackageFqName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {
|
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {
|
||||||
index.indexedRoots.forEach {
|
index.indexedRoots.forEach {
|
||||||
@@ -225,6 +225,14 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val appendJavaSourceRootsHandler = fun(roots: List<File>) {
|
||||||
|
updateClasspath(roots.map { JavaSourceRoot(it, null) })
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
project.putUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY, appendJavaSourceRootsHandler)
|
||||||
|
}
|
||||||
|
|
||||||
fun updateClasspath(roots: List<ContentRoot>): List<File>? {
|
fun updateClasspath(roots: List<ContentRoot>): List<File>? {
|
||||||
return rootsIndex.addNewIndexForRoots(roots.classpathRoots())?.let {
|
return rootsIndex.addNewIndexForRoots(roots.classpathRoots())?.let {
|
||||||
updateClasspathFromRootsIndex(it)
|
updateClasspathFromRootsIndex(it)
|
||||||
|
|||||||
+3
-1
@@ -136,7 +136,9 @@ object KotlinToJVMBytecodeCompiler {
|
|||||||
projectConfiguration.addJavaSourceRoots(result.additionalJavaRoots)
|
projectConfiguration.addJavaSourceRoots(result.additionalJavaRoots)
|
||||||
projectConfiguration.isReadOnly = oldReadOnlyValue
|
projectConfiguration.isReadOnly = oldReadOnlyValue
|
||||||
|
|
||||||
environment.updateClasspath(result.additionalJavaRoots.map { JavaSourceRoot(it, null) })
|
if (result.addToEnvironment) {
|
||||||
|
environment.updateClasspath(result.additionalJavaRoots.map { JavaSourceRoot(it, null) })
|
||||||
|
}
|
||||||
|
|
||||||
// Clear package caches (see KotlinJavaPsiFacade)
|
// Clear package caches (see KotlinJavaPsiFacade)
|
||||||
(PsiManager.getInstance(environment.project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter()
|
(PsiManager.getInstance(environment.project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter()
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ open class AnalysisResult protected constructor(
|
|||||||
class RetryWithAdditionalJavaRoots(
|
class RetryWithAdditionalJavaRoots(
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
moduleDescriptor: ModuleDescriptor,
|
||||||
val additionalJavaRoots: List<File>
|
val additionalJavaRoots: List<File>,
|
||||||
|
val addToEnvironment: Boolean = true
|
||||||
) : AnalysisResult(bindingContext, moduleDescriptor)
|
) : AnalysisResult(bindingContext, moduleDescriptor)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.config
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY = Key<(List<File>) -> Unit>("AppendJavaSourceRootsHandlerKey")
|
||||||
+131
-107
@@ -19,18 +19,16 @@ package org.jetbrains.kotlin.annotation
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.StandardFileSystems
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
|
import org.jetbrains.kotlin.annotation.processing.RoundAnnotations
|
||||||
import org.jetbrains.kotlin.annotation.processing.diagnostic.ErrorsAnnotationProcessing
|
import org.jetbrains.kotlin.annotation.processing.diagnostic.ErrorsAnnotationProcessing
|
||||||
import org.jetbrains.kotlin.annotation.processing.impl.*
|
import org.jetbrains.kotlin.annotation.processing.impl.*
|
||||||
import org.jetbrains.kotlin.asJava.findFacadeClass
|
import org.jetbrains.kotlin.config.APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.java.model.elements.JeTypeElement
|
import org.jetbrains.kotlin.java.model.elements.JeTypeElement
|
||||||
import org.jetbrains.kotlin.java.model.internal.getAnnotationsWithInherited
|
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -65,6 +63,8 @@ abstract class AbstractAnnotationProcessingExtension(
|
|||||||
if (verboseOutput) messager.printMessage(Diagnostic.Kind.OTHER, "Kapt: " + message())
|
if (verboseOutput) messager.printMessage(Diagnostic.Kind.OTHER, "Kapt: " + message())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Int.count(noun: String) = if (this == 1) "$this $noun" else "$this ${noun}s"
|
||||||
|
|
||||||
override fun analysisCompleted(
|
override fun analysisCompleted(
|
||||||
project: Project,
|
project: Project,
|
||||||
module: ModuleDescriptor,
|
module: ModuleDescriptor,
|
||||||
@@ -81,50 +81,143 @@ abstract class AbstractAnnotationProcessingExtension(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
log { "Analysing Kotlin files: " + files.map { it.virtualFile.path } }
|
val appendJavaSourceRootsHandler = project.getUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY)
|
||||||
|
if (appendJavaSourceRootsHandler == null) {
|
||||||
val analysisContext = AnalysisContext(hashMapOf())
|
log { "Java source root handler is not set, exiting" }
|
||||||
analysisContext.analyzeFiles(files)
|
return null
|
||||||
|
}
|
||||||
log { "Analysing Java source roots: $javaSourceRoots" }
|
|
||||||
|
|
||||||
val psiManager = PsiManager.getInstance(project)
|
val psiManager = PsiManager.getInstance(project)
|
||||||
for (javaSourceRoot in javaSourceRoots) {
|
val javaPsiFacade = JavaPsiFacade.getInstance(project)
|
||||||
javaSourceRoot.walk().filter { it.isFile && it.extension == "java" }.forEach {
|
val projectScope = GlobalSearchScope.projectScope(project)
|
||||||
val vFile = StandardFileSystems.local().findFileByPath(it.absolutePath)
|
|
||||||
if (vFile != null) {
|
|
||||||
val javaFile = psiManager.findFile(vFile) as? PsiJavaFile
|
|
||||||
if (javaFile != null) {
|
|
||||||
analysisContext.analyzeFile(javaFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val options = emptyMap<String, String>()
|
val options = emptyMap<String, String>()
|
||||||
log { "Options: $options" }
|
log { "Options: $options" }
|
||||||
|
|
||||||
val javaPsiFacade = JavaPsiFacade.getInstance(project)
|
|
||||||
val projectScope = GlobalSearchScope.projectScope(project)
|
|
||||||
|
|
||||||
val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir)
|
val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir)
|
||||||
val types = KotlinTypes(javaPsiFacade, PsiManager.getInstance(project), projectScope)
|
val types = KotlinTypes(javaPsiFacade, PsiManager.getInstance(project), projectScope)
|
||||||
val elements = KotlinElements(javaPsiFacade, projectScope)
|
val elements = KotlinElements(javaPsiFacade, projectScope)
|
||||||
|
|
||||||
val processingEnvironment = KotlinProcessingEnvironment(elements, types, messager, options, filer)
|
val processingEnvironment = KotlinProcessingEnvironment(
|
||||||
processors.forEach { it.init(processingEnvironment) }
|
elements, types, messager, options, filer, processors,
|
||||||
|
project, psiManager, javaPsiFacade, projectScope, appendJavaSourceRootsHandler)
|
||||||
|
|
||||||
log { "Initialized processors: " + processors.joinToString { it.javaClass.name } }
|
val processingResult = processingEnvironment.doAnnotationProcessing(files)
|
||||||
|
|
||||||
|
annotationProcessingComplete = true
|
||||||
|
log {
|
||||||
|
"Annotation processing complete, " +
|
||||||
|
processingResult.errorCount.count("error") + ", " +
|
||||||
|
processingResult.warningCount.count("warning")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processingResult.errorCount != 0) {
|
||||||
|
val reportFile = files.firstOrNull()
|
||||||
|
if (reportFile != null) {
|
||||||
|
bindingTrace.report(ErrorsAnnotationProcessing.ANNOTATION_PROCESSING_ERROR.on(reportFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not restart Kotlin file analysis
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processingResult.wasAnythingGenerated) {
|
||||||
|
// Nothing was generated, do not need to restart analysis
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnalysisResult.RetryWithAdditionalJavaRoots(
|
||||||
|
bindingTrace.bindingContext,
|
||||||
|
module,
|
||||||
|
listOf(generatedSourcesOutputDir),
|
||||||
|
addToEnvironment = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinProcessingEnvironment.doAnnotationProcessing(files: Collection<KtFile>): ProcessingResult {
|
||||||
|
run initializeProcessors@ {
|
||||||
|
processors.forEach { it.init(this) }
|
||||||
|
log { "Initialized processors: " + processors.joinToString { it.javaClass.name } }
|
||||||
|
}
|
||||||
|
|
||||||
|
val firstRoundAnnotations = RoundAnnotations()
|
||||||
|
|
||||||
|
run analyzeFilesForFirstRound@ {
|
||||||
|
log { "Analysing Kotlin files: " + files.map { it.virtualFile.path } }
|
||||||
|
firstRoundAnnotations.analyzeFiles(files)
|
||||||
|
|
||||||
|
log { "Analysing Java source roots: $javaSourceRoots" }
|
||||||
|
for (javaSourceRoot in javaSourceRoots) {
|
||||||
|
javaSourceRoot.walk().filter { it.isFile && it.extension == "java" }.forEach {
|
||||||
|
val vFile = StandardFileSystems.local().findFileByPath(it.absolutePath)
|
||||||
|
if (vFile != null) {
|
||||||
|
val javaFile = psiManager.findFile(vFile) as? PsiJavaFile
|
||||||
|
if (javaFile != null) {
|
||||||
|
firstRoundAnnotations.analyzeFile(javaFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val finalRoundNumber = run annotationProcessing@ {
|
||||||
|
val firstRoundEnvironment = KotlinRoundEnvironment(firstRoundAnnotations, false, 1)
|
||||||
|
process(firstRoundEnvironment)
|
||||||
|
} + 1
|
||||||
|
|
||||||
|
log { "Starting round $finalRoundNumber (final)" }
|
||||||
|
val finalRoundEnvironment = KotlinRoundEnvironment(RoundAnnotations(), true, finalRoundNumber)
|
||||||
|
for (processor in processors) {
|
||||||
|
processor.process(emptySet(), finalRoundEnvironment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProcessingResult(messager.errorCount, messager.warningCount, filer.wasAnythingGenerated)
|
||||||
|
}
|
||||||
|
|
||||||
|
private tailrec fun KotlinProcessingEnvironment.process(roundEnvironment: KotlinRoundEnvironment): Int {
|
||||||
|
val newFiles = doRound(roundEnvironment)
|
||||||
|
val newJavaFiles = newFiles.filter { it.extension.toLowerCase() == "java" }
|
||||||
|
if (newJavaFiles.isEmpty() || messager.errorCount != 0) {
|
||||||
|
return roundEnvironment.roundNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new Java source roots after the first round
|
||||||
|
if (roundEnvironment.roundNumber == 1) {
|
||||||
|
appendJavaSourceRootsHandler(listOf(generatedSourcesOutputDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the platform caches
|
||||||
|
(PsiManager.getInstance(project).modificationTracker as? PsiModificationTrackerImpl)?.incCounter()
|
||||||
|
|
||||||
|
// Find generated files
|
||||||
|
val localFileSystem = StandardFileSystems.local()
|
||||||
|
val psiFiles = newJavaFiles
|
||||||
|
.map { localFileSystem.findFileByPath(it.absolutePath)?.let { psiManager.findFile(it) } }
|
||||||
|
.filterIsInstance<PsiJavaFile>()
|
||||||
|
|
||||||
|
if (psiFiles.isEmpty()) {
|
||||||
|
log { "Something is strange, files were generated but not found by PsiManager" }
|
||||||
|
return roundEnvironment.roundNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the next round
|
||||||
|
val nextRoundAnnotations = RoundAnnotations().apply { analyzeFiles(psiFiles) }
|
||||||
|
val nextRoundEnvironment = KotlinRoundEnvironment(nextRoundAnnotations, false, roundEnvironment.roundNumber + 1)
|
||||||
|
return process(nextRoundEnvironment)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinProcessingEnvironment.doRound(roundEnvironment: KotlinRoundEnvironment): List<File> {
|
||||||
|
log { "Starting round ${roundEnvironment.roundNumber}" }
|
||||||
|
|
||||||
|
val newFiles = mutableListOf<File>()
|
||||||
|
filer.onFileCreatedHandler = { newFiles += it }
|
||||||
|
|
||||||
log { "Starting round 1" }
|
|
||||||
val round1Environment = KotlinRoundEnvironment(analysisContext)
|
|
||||||
for (processor in processors) {
|
for (processor in processors) {
|
||||||
val supportedAnnotationNames = processor.supportedAnnotationTypes
|
val supportedAnnotationNames = processor.supportedAnnotationTypes
|
||||||
val supportsAnyAnnotation = supportedAnnotationNames.contains("*")
|
val acceptsAnyAnnotation = supportedAnnotationNames.contains("*")
|
||||||
|
|
||||||
val applicableAnnotationNames = when (supportsAnyAnnotation) {
|
val applicableAnnotationNames = when (acceptsAnyAnnotation) {
|
||||||
true -> analysisContext.annotationsMap.keys
|
true -> roundEnvironment.annotationsMap.keys
|
||||||
false -> processor.supportedAnnotationTypes.filter { it in analysisContext.annotationsMap }
|
false -> processor.supportedAnnotationTypes.filter { it in roundEnvironment.annotationsMap }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applicableAnnotationNames.isEmpty()) {
|
if (applicableAnnotationNames.isEmpty()) {
|
||||||
@@ -141,83 +234,14 @@ abstract class AbstractAnnotationProcessingExtension(
|
|||||||
"Processing with " + processor.javaClass.name + " (annotations: " + annotationNames + ")"
|
"Processing with " + processor.javaClass.name + " (annotations: " + annotationNames + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.process(applicableAnnotations, round1Environment)
|
processor.process(applicableAnnotations, roundEnvironment)
|
||||||
}
|
}
|
||||||
|
|
||||||
log { "Starting round 2 (final)" }
|
log { "Round ${roundEnvironment.roundNumber} finished, ${newFiles.size.count("file")} generated (${newFiles.joinToString()})" }
|
||||||
val round2Environment = KotlinRoundEnvironment(AnalysisContext(hashMapOf()), isProcessingOver = true)
|
return newFiles
|
||||||
for (processor in processors) {
|
|
||||||
processor.process(emptySet(), round2Environment)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Int.count(noun: String) = if (this == 1) "$this $noun" else "$this ${noun}s"
|
|
||||||
log { "Annotation processing complete, ${messager.errorCount.count("error")}, ${messager.warningCount.count("warning")}" }
|
|
||||||
|
|
||||||
if (messager.errorCount != 0) {
|
|
||||||
val reportFile = files.firstOrNull()
|
|
||||||
if (reportFile != null) {
|
|
||||||
bindingTrace.report(ErrorsAnnotationProcessing.ANNOTATION_PROCESSING_ERROR.on(reportFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not restart analysis
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
annotationProcessingComplete = true
|
|
||||||
return AnalysisResult.RetryWithAdditionalJavaRoots(bindingTrace.bindingContext, module, listOf(generatedSourcesOutputDir))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun loadAnnotationProcessors(): List<Processor>
|
protected abstract fun loadAnnotationProcessors(): List<Processor>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class AnalysisContext(annotationsMap: MutableMap<String, MutableList<PsiModifierListOwner>>) {
|
private class ProcessingResult(val errorCount: Int, val warningCount: Int, val wasAnythingGenerated: Boolean)
|
||||||
private val mutableAnnotationsMap = annotationsMap
|
|
||||||
|
|
||||||
val annotationsMap: Map<String, List<PsiModifierListOwner>>
|
|
||||||
get() = mutableAnnotationsMap
|
|
||||||
|
|
||||||
fun analyzeFiles(files: Collection<KtFile>) {
|
|
||||||
for (file in files) {
|
|
||||||
analyzeFile(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun analyzeFile(file: KtFile) {
|
|
||||||
val lightClass = file.findFacadeClass()
|
|
||||||
|
|
||||||
if (lightClass != null) {
|
|
||||||
analyzeDeclaration(lightClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (declaration in file.declarations) {
|
|
||||||
if (declaration !is KtClassOrObject) continue
|
|
||||||
val clazz = declaration.toLightClass() ?: continue
|
|
||||||
analyzeDeclaration(clazz)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun analyzeFile(file: PsiJavaFile) {
|
|
||||||
file.classes.forEach { analyzeDeclaration(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun analyzeDeclaration(declaration: PsiElement) {
|
|
||||||
if (declaration !is PsiModifierListOwner) return
|
|
||||||
|
|
||||||
for (annotation in declaration.getAnnotationsWithInherited()) {
|
|
||||||
val fqName = annotation.qualifiedName ?: return
|
|
||||||
mutableAnnotationsMap.getOrPut(fqName, { mutableListOf() }).add(declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (declaration is PsiClass) {
|
|
||||||
declaration.methods.forEach { analyzeDeclaration(it) }
|
|
||||||
declaration.fields.forEach { analyzeDeclaration(it) }
|
|
||||||
declaration.innerClasses.forEach { analyzeDeclaration(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (declaration is PsiMethod) {
|
|
||||||
for (parameter in declaration.parameterList.parameters) {
|
|
||||||
analyzeDeclaration(parameter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.annotation.processing
|
||||||
|
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import org.jetbrains.kotlin.asJava.findFacadeClass
|
||||||
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
|
import org.jetbrains.kotlin.java.model.internal.getAnnotationsWithInherited
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
internal class RoundAnnotations() {
|
||||||
|
private val mutableAnnotationsMap = mutableMapOf<String, MutableList<PsiModifierListOwner>>()
|
||||||
|
|
||||||
|
val annotationsMap: Map<String, List<PsiModifierListOwner>>
|
||||||
|
get() = mutableAnnotationsMap
|
||||||
|
|
||||||
|
fun analyzeFiles(files: Collection<KtFile>) = files.forEach { analyzeFile(it) }
|
||||||
|
|
||||||
|
@JvmName("analyzePsiJavaFiles")
|
||||||
|
fun analyzeFiles(files: Collection<PsiJavaFile>) = files.forEach { analyzeFile(it) }
|
||||||
|
|
||||||
|
fun analyzeFile(file: KtFile) {
|
||||||
|
val lightClass = file.findFacadeClass()
|
||||||
|
|
||||||
|
if (lightClass != null) {
|
||||||
|
analyzeDeclaration(lightClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (declaration in file.declarations) {
|
||||||
|
if (declaration !is KtClassOrObject) continue
|
||||||
|
val clazz = declaration.toLightClass() ?: continue
|
||||||
|
analyzeDeclaration(clazz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun analyzeFile(file: PsiJavaFile) {
|
||||||
|
file.classes.forEach { analyzeDeclaration(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun analyzeDeclaration(declaration: PsiElement) {
|
||||||
|
if (declaration !is PsiModifierListOwner) return
|
||||||
|
|
||||||
|
for (annotation in declaration.getAnnotationsWithInherited()) {
|
||||||
|
val fqName = annotation.qualifiedName ?: return
|
||||||
|
mutableAnnotationsMap.getOrPut(fqName, { mutableListOf() }).add(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaration is PsiClass) {
|
||||||
|
declaration.methods.forEach { analyzeDeclaration(it) }
|
||||||
|
declaration.fields.forEach { analyzeDeclaration(it) }
|
||||||
|
declaration.innerClasses.forEach { analyzeDeclaration(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaration is PsiMethod) {
|
||||||
|
for (parameter in declaration.parameterList.parameters) {
|
||||||
|
analyzeDeclaration(parameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
-4
@@ -24,11 +24,18 @@ import javax.tools.JavaFileManager
|
|||||||
import javax.tools.JavaFileObject
|
import javax.tools.JavaFileObject
|
||||||
import javax.tools.StandardLocation
|
import javax.tools.StandardLocation
|
||||||
|
|
||||||
class KotlinFiler(val generatedSourceDir: File, val classesOutputDir: File) : Filer {
|
class KotlinFiler(
|
||||||
|
val generatedSourceDir: File,
|
||||||
|
val classesOutputDir: File,
|
||||||
|
internal var onFileCreatedHandler: (File) -> Unit = {}
|
||||||
|
) : Filer {
|
||||||
private companion object {
|
private companion object {
|
||||||
val PACKAGE_INFO_SUFFIX = ".packageInfo"
|
val PACKAGE_INFO_SUFFIX = ".packageInfo"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal var wasAnythingGenerated: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
private fun getGeneratedFile(nameCharSequence: CharSequence, extension: String): File {
|
private fun getGeneratedFile(nameCharSequence: CharSequence, extension: String): File {
|
||||||
val name = nameCharSequence.toString()
|
val name = nameCharSequence.toString()
|
||||||
val isPackageInfo = name.endsWith(PACKAGE_INFO_SUFFIX)
|
val isPackageInfo = name.endsWith(PACKAGE_INFO_SUFFIX)
|
||||||
@@ -47,8 +54,13 @@ class KotlinFiler(val generatedSourceDir: File, val classesOutputDir: File) : Fi
|
|||||||
return File(packageDir, fileName)
|
return File(packageDir, fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun File.notifyCreated() = apply {
|
||||||
|
wasAnythingGenerated = true
|
||||||
|
onFileCreatedHandler(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun createSourceFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
|
override fun createSourceFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
|
||||||
return KotlinJavaFileObject(getGeneratedFile(name, ".java"))
|
return KotlinJavaFileObject(getGeneratedFile(name, ".java").notifyCreated())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResource(location: JavaFileManager.Location, pkg: CharSequence, relativeName: CharSequence): FileObject? {
|
override fun getResource(location: JavaFileManager.Location, pkg: CharSequence, relativeName: CharSequence): FileObject? {
|
||||||
@@ -63,7 +75,7 @@ class KotlinFiler(val generatedSourceDir: File, val classesOutputDir: File) : Fi
|
|||||||
): FileObject? {
|
): FileObject? {
|
||||||
val resourceFile = getResourceFile(location, pkg, relativeName)
|
val resourceFile = getResourceFile(location, pkg, relativeName)
|
||||||
resourceFile.parentFile.mkdirs()
|
resourceFile.parentFile.mkdirs()
|
||||||
return KotlinFileObject(resourceFile)
|
return KotlinFileObject(resourceFile.notifyCreated())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getResourceFile(location: JavaFileManager.Location, pkg: CharSequence, relativeName: CharSequence): File {
|
private fun getResourceFile(location: JavaFileManager.Location, pkg: CharSequence, relativeName: CharSequence): File {
|
||||||
@@ -80,6 +92,6 @@ class KotlinFiler(val generatedSourceDir: File, val classesOutputDir: File) : Fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createClassFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
|
override fun createClassFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
|
||||||
return KotlinJavaFileObject(getGeneratedFile(name, ".class"))
|
return KotlinJavaFileObject(getGeneratedFile(name, ".class").notifyCreated())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+16
-4
@@ -16,10 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.annotation.processing.impl
|
package org.jetbrains.kotlin.annotation.processing.impl
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.JavaPsiFacade
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.annotation.processing.Filer
|
|
||||||
import javax.annotation.processing.Messager
|
|
||||||
import javax.annotation.processing.ProcessingEnvironment
|
import javax.annotation.processing.ProcessingEnvironment
|
||||||
|
import javax.annotation.processing.Processor
|
||||||
import javax.lang.model.SourceVersion
|
import javax.lang.model.SourceVersion
|
||||||
import javax.lang.model.util.Elements
|
import javax.lang.model.util.Elements
|
||||||
import javax.lang.model.util.Types
|
import javax.lang.model.util.Types
|
||||||
@@ -27,9 +31,17 @@ import javax.lang.model.util.Types
|
|||||||
class KotlinProcessingEnvironment(
|
class KotlinProcessingEnvironment(
|
||||||
private val elements: Elements,
|
private val elements: Elements,
|
||||||
private val types: Types,
|
private val types: Types,
|
||||||
private val messager: Messager,
|
private val messager: KotlinMessager,
|
||||||
options: Map<String, String>,
|
options: Map<String, String>,
|
||||||
private val filer: Filer
|
private val filer: KotlinFiler,
|
||||||
|
|
||||||
|
internal val processors: List<Processor>,
|
||||||
|
|
||||||
|
internal val project: Project,
|
||||||
|
internal val psiManager: PsiManager,
|
||||||
|
internal val javaPsiFacade: JavaPsiFacade,
|
||||||
|
internal val projectScope: GlobalSearchScope,
|
||||||
|
internal val appendJavaSourceRootsHandler: (List<File>) -> Unit
|
||||||
) : ProcessingEnvironment {
|
) : ProcessingEnvironment {
|
||||||
private val options = Collections.unmodifiableMap(options)
|
private val options = Collections.unmodifiableMap(options)
|
||||||
|
|
||||||
|
|||||||
+10
-4
@@ -16,23 +16,29 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.annotation.processing.impl
|
package org.jetbrains.kotlin.annotation.processing.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.annotation.AnalysisContext
|
import com.intellij.psi.PsiModifierListOwner
|
||||||
|
import org.jetbrains.kotlin.annotation.processing.RoundAnnotations
|
||||||
import org.jetbrains.kotlin.java.model.toJeElement
|
import org.jetbrains.kotlin.java.model.toJeElement
|
||||||
import javax.annotation.processing.RoundEnvironment
|
import javax.annotation.processing.RoundEnvironment
|
||||||
import javax.lang.model.element.Element
|
import javax.lang.model.element.Element
|
||||||
import javax.lang.model.element.TypeElement
|
import javax.lang.model.element.TypeElement
|
||||||
|
|
||||||
internal class KotlinRoundEnvironment(
|
internal class KotlinRoundEnvironment(
|
||||||
private val context: AnalysisContext,
|
private val roundAnnotations: RoundAnnotations,
|
||||||
private val isProcessingOver: Boolean = false) : RoundEnvironment {
|
private val isProcessingOver: Boolean,
|
||||||
|
internal val roundNumber: Int
|
||||||
|
) : RoundEnvironment {
|
||||||
private var isError = false
|
private var isError = false
|
||||||
|
|
||||||
|
internal val annotationsMap: Map<String, List<PsiModifierListOwner>>
|
||||||
|
get() = roundAnnotations.annotationsMap
|
||||||
|
|
||||||
override fun getRootElements() = emptySet<Element>()
|
override fun getRootElements() = emptySet<Element>()
|
||||||
|
|
||||||
override fun processingOver() = isProcessingOver
|
override fun processingOver() = isProcessingOver
|
||||||
|
|
||||||
private fun getElementsAnnotatedWith(fqName: String): Set<Element> {
|
private fun getElementsAnnotatedWith(fqName: String): Set<Element> {
|
||||||
val declarations = context.annotationsMap[fqName] ?: return emptySet()
|
val declarations = roundAnnotations.annotationsMap[fqName] ?: return emptySet()
|
||||||
return hashSetOf<Element>().apply {
|
return hashSetOf<Element>().apply {
|
||||||
for (declaration in declarations) {
|
for (declaration in declarations) {
|
||||||
declaration.toJeElement()?.let { add(it) }
|
declaration.toJeElement()?.let { add(it) }
|
||||||
|
|||||||
Reference in New Issue
Block a user