From 6abde4223bf8e657c94cf84a01132aa2719c956e Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 1 Dec 2016 17:43:00 +0300 Subject: [PATCH] AllOpen: Add IDE integration --- .idea/artifacts/KotlinPlugin.xml | 183 +++++++++--------- .idea/modules.xml | 4 +- build.xml | 4 +- .../jetbrains/kotlin/utils/KotlinPaths.java | 3 + .../kotlin/utils/KotlinPathsFromHomeDir.java | 6 + .../org/jetbrains/kotlin/utils/PathUtil.java | 1 + .../kotlin/generators/tests/GenerateTests.kt | 2 +- .../kotlin/idea/maven/KotlinMavenImporter.kt | 36 ++-- idea/src/META-INF/extensions/ide.xml | 4 + idea/src/META-INF/gradle.xml | 19 +- idea/src/META-INF/maven.xml | 9 +- idea/src/META-INF/plugin.xml | 2 + .../KotlinGradleProjectDataService.kt | 15 +- .../gradle/KotlinGradleInspectionVisitor.kt | 16 +- libraries/tools/kotlin-allopen/pom.xml | 2 +- .../kotlin/allopen/gradle/AllOpenSubplugin.kt | 39 +++- .../allopen-cli.iml} | 0 ...penDeclarationAttributeAltererExtension.kt | 36 ++-- .../{ => allopen-cli}/src/AllOpenPlugin.kt | 6 +- ...otlin.compiler.plugin.CommandLineProcessor | 0 ....kotlin.compiler.plugin.ComponentRegistrar | 0 .../bytecodeListing/allOpenOnNotClasses.kt | 0 .../bytecodeListing/allOpenOnNotClasses.txt | 0 .../testData/bytecodeListing/alreadyOpen.kt | 0 .../testData/bytecodeListing/alreadyOpen.txt | 0 .../testData/bytecodeListing/explicitFinal.kt | 18 ++ .../bytecodeListing/explicitFinal.txt | 5 +- .../bytecodeListing/metaAnnotation.kt | 0 .../bytecodeListing/metaAnnotation.txt | 0 .../testData/bytecodeListing/nestedInner.kt | 0 .../testData/bytecodeListing/nestedInner.txt | 0 .../testData/bytecodeListing/noAllOpen.kt | 0 .../testData/bytecodeListing/noAllOpen.txt | 0 .../testData/bytecodeListing/sealed.kt | 0 .../testData/bytecodeListing/sealed.txt | 0 .../bytecodeListing/severalAllOpen.kt | 0 .../bytecodeListing/severalAllOpen.txt | 0 .../testData/bytecodeListing/simple.kt | 0 .../testData/bytecodeListing/simple.txt | 0 .../bytecodeListing/superClassAnnotation.kt | 6 +- .../bytecodeListing/superClassAnnotation.txt | 6 +- plugins/allopen/allopen-ide/allopen-ide.iml | 23 +++ .../src/AllOpenGradleProjectImportHandler.kt | 29 +++ .../src/AllOpenMavenProjectImportHandler.kt | 56 ++++++ ...penDeclarationAttributeAltererExtension.kt | 61 ++++++ .../testData/bytecodeListing/explicitFinal.kt | 13 -- ...ion-based-compiler-plugins-ide-support.iml | 21 ++ .../src/AbstractGradleImportHandler.kt | 66 +++++++ .../src/AbstractMavenImportHandler.kt | 78 ++++++++ .../src/idePluginUtils.kt | 52 +++++ plugins/plugins-tests/plugins-tests.iml | 2 +- .../AbstractBytecodeListingTestForAllOpen.kt | 4 +- ...ytecodeListingTestForAllOpenGenerated.java | 25 +-- 53 files changed, 677 insertions(+), 175 deletions(-) rename plugins/allopen/{allopen.iml => allopen-cli/allopen-cli.iml} (100%) rename plugins/allopen/{ => allopen-cli}/src/AllOpenDeclarationAttributeAltererExtension.kt (64%) rename plugins/allopen/{ => allopen-cli}/src/AllOpenPlugin.kt (93%) rename plugins/allopen/{ => allopen-cli}/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor (100%) rename plugins/allopen/{ => allopen-cli}/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/allOpenOnNotClasses.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/allOpenOnNotClasses.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/alreadyOpen.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/alreadyOpen.txt (100%) create mode 100644 plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.kt rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/explicitFinal.txt (62%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/metaAnnotation.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/metaAnnotation.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/nestedInner.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/nestedInner.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/noAllOpen.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/noAllOpen.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/sealed.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/sealed.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/severalAllOpen.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/severalAllOpen.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/simple.kt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/simple.txt (100%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/superClassAnnotation.kt (72%) rename plugins/allopen/{ => allopen-cli}/testData/bytecodeListing/superClassAnnotation.txt (85%) create mode 100644 plugins/allopen/allopen-ide/allopen-ide.iml create mode 100644 plugins/allopen/allopen-ide/src/AllOpenGradleProjectImportHandler.kt create mode 100644 plugins/allopen/allopen-ide/src/AllOpenMavenProjectImportHandler.kt create mode 100644 plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt delete mode 100644 plugins/allopen/testData/bytecodeListing/explicitFinal.kt create mode 100644 plugins/annotation-based-compiler-plugins-ide-support/annotation-based-compiler-plugins-ide-support.iml create mode 100644 plugins/annotation-based-compiler-plugins-ide-support/src/AbstractGradleImportHandler.kt create mode 100644 plugins/annotation-based-compiler-plugins-ide-support/src/AbstractMavenImportHandler.kt create mode 100644 plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml index f6d23649e60..91e7055017f 100644 --- a/.idea/artifacts/KotlinPlugin.xml +++ b/.idea/artifacts/KotlinPlugin.xml @@ -1,90 +1,97 @@ - - $PROJECT_DIR$/out/artifacts/Kotlin - - - file://$PROJECT_DIR$/idea-runner/runner.xml - copy-runtime-for-idea-plugin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + $PROJECT_DIR$/out/artifacts/Kotlin + + + file://$PROJECT_DIR$/idea-runner/runner.xml + copy-runtime-for-idea-plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 27f12a3cb19..b0a44652e07 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,13 +3,15 @@ - + + + diff --git a/build.xml b/build.xml index 427843676c3..45071f459ee 100644 --- a/build.xml +++ b/build.xml @@ -828,7 +828,7 @@ - + @@ -839,7 +839,7 @@ - + diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPaths.java b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPaths.java index 462a2f130dd..787c6c8ad21 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPaths.java +++ b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPaths.java @@ -48,6 +48,9 @@ public interface KotlinPaths { @NotNull File getJsStdLibSrcJarPath(); + @NotNull + File getAllOpenPluginJarPath(); + @NotNull File getCompilerPath(); diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPathsFromHomeDir.java b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPathsFromHomeDir.java index cb6adbc0e75..afd05e5ebe6 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPathsFromHomeDir.java +++ b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinPathsFromHomeDir.java @@ -82,6 +82,12 @@ public class KotlinPathsFromHomeDir implements KotlinPaths { return getLibraryFile(PathUtil.JS_LIB_SRC_JAR_NAME); } + @NotNull + @Override + public File getAllOpenPluginJarPath() { + return getLibraryFile(PathUtil.ALLOPEN_PLUGIN_JAR_NAME); + } + @NotNull @Override public File getCompilerPath() { diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.java b/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.java index 8e2469f166b..3bce0e86d01 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.java +++ b/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.java @@ -29,6 +29,7 @@ public class PathUtil { public static final String JPS_KOTLIN_HOME_PROPERTY = "jps.kotlin.home"; public static final String JS_LIB_JAR_NAME = "kotlin-jslib.jar"; + public static final String ALLOPEN_PLUGIN_JAR_NAME = "allopen-compiler-plugin.jar"; public static final String JS_LIB_SRC_JAR_NAME = "kotlin-jslib-sources.jar"; public static final String KOTLIN_JAVA_RUNTIME_JAR = "kotlin-runtime.jar"; public static final String KOTLIN_JAVA_REFLECT_JAR = "kotlin-reflect.jar"; diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 3bb8ce6112c..9c8de1e3c6b 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -1159,7 +1159,7 @@ fun main(args: Array) { } } - testGroup("plugins/plugins-tests/tests", "plugins/allopen/testData") { + testGroup("plugins/plugins-tests/tests", "plugins/allopen/allopen-cli/testData") { testClass() { model("bytecodeListing", extension = "kt") } diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt index d6a249abf64..6fdc2cb05c9 100644 --- a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt @@ -33,21 +33,31 @@ import org.jetbrains.idea.maven.model.MavenPlugin import org.jetbrains.idea.maven.project.* import org.jetbrains.jps.model.java.JavaSourceRootType import org.jetbrains.jps.model.module.JpsModuleSourceRootType -import org.jetbrains.kotlin.config.LanguageVersion -import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.configureFacet import org.jetbrains.kotlin.idea.facet.getOrCreateFacet -import org.jetbrains.kotlin.idea.facet.initializeIfNeeded -import org.jetbrains.kotlin.idea.facet.mavenLibraryId import org.jetbrains.kotlin.idea.maven.configuration.KotlinMavenConfigurator import java.io.File import java.util.* -private val KotlinPluginGroupId = "org.jetbrains.kotlin" -private val KotlinPluginArtifactId = "kotlin-maven-plugin" -private val KotlinPluginSourceDirsConfig = "sourceDirs" +interface MavenProjectImportHandler { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.mavenProjectImportHandler", + MavenProjectImportHandler::class.java + ) + + operator fun invoke(facet: KotlinFacet, mavenProject: MavenProject) +} + +class KotlinMavenImporter : MavenImporter(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID) { + companion object { + val KOTLIN_PLUGIN_GROUP_ID = "org.jetbrains.kotlin" + val KOTLIN_PLUGIN_ARTIFACT_ID = "kotlin-maven-plugin" + + val KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG = "sourceDirs" + } -class KotlinMavenImporter : MavenImporter(KotlinPluginGroupId, KotlinPluginArtifactId) { override fun preProcess(module: Module, mavenProject: MavenProject, changes: MavenProjectChanges, modifiableModelsProvider: IdeModifiableModelsProvider) { } @@ -70,7 +80,7 @@ class KotlinMavenImporter : MavenImporter(KotlinPluginGroupId, KotlinPluginArtif if (changes.dependencies) { // TODO: here we have to process all kotlin libraries but for now we only handle standard libraries - val artifacts = mavenProject.dependencyArtifactIndex.data[KotlinPluginGroupId]?.values?.flatMap { it.filter { it.isResolved } } ?: emptyList() + val artifacts = mavenProject.dependencyArtifactIndex.data[KOTLIN_PLUGIN_GROUP_ID]?.values?.flatMap { it.filter { it.isResolved } } ?: emptyList() val librariesWithNoSources = ArrayList() OrderEnumerator.orderEntries(module).forEachLibrary { library -> @@ -93,7 +103,9 @@ class KotlinMavenImporter : MavenImporter(KotlinPluginGroupId, KotlinPluginArtif private fun configureFacet(mavenProject: MavenProject, modifiableModelsProvider: IdeModifiableModelsProvider, module: Module) { val compilerVersion = mavenProject.findPlugin(KotlinMavenConfigurator.GROUP_ID, KotlinMavenConfigurator.MAVEN_PLUGIN_ID)?.version ?: return - module.getOrCreateFacet(modifiableModelsProvider).configureFacet(compilerVersion, modifiableModelsProvider) + val kotlinFacet = module.getOrCreateFacet(modifiableModelsProvider) + kotlinFacet.configureFacet(compilerVersion, modifiableModelsProvider) + MavenProjectImportHandler.getInstances(module.project).forEach { it(kotlinFacet, mavenProject) } } // TODO in theory it should work like this but it doesn't as it couldn't unmark source roots that are not roots anymore. @@ -143,8 +155,8 @@ class KotlinMavenImporter : MavenImporter(KotlinPluginGroupId, KotlinPluginArtif }.distinct() } -private fun MavenPlugin.isKotlinPlugin() = groupId == KotlinPluginGroupId && artifactId == KotlinPluginArtifactId -private fun Element?.sourceDirectories(): List = this?.getChildren(KotlinPluginSourceDirsConfig)?.flatMap { it.children ?: emptyList() }?.map { it.textTrim } ?: emptyList() +private fun MavenPlugin.isKotlinPlugin() = groupId == KotlinMavenImporter.KOTLIN_PLUGIN_GROUP_ID && artifactId == KotlinMavenImporter.KOTLIN_PLUGIN_ARTIFACT_ID +private fun Element?.sourceDirectories(): List = this?.getChildren(KotlinMavenImporter.KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG)?.flatMap { it.children ?: emptyList() }?.map { it.textTrim } ?: emptyList() private fun MavenPlugin.Execution.sourceType() = goals.map { if (isTestGoalName(it)) SourceType.TEST else SourceType.PROD } .distinct() diff --git a/idea/src/META-INF/extensions/ide.xml b/idea/src/META-INF/extensions/ide.xml index 6d1698ca399..228c42d5e73 100644 --- a/idea/src/META-INF/extensions/ide.xml +++ b/idea/src/META-INF/extensions/ide.xml @@ -2,6 +2,10 @@ + + diff --git a/idea/src/META-INF/gradle.xml b/idea/src/META-INF/gradle.xml index e3d118e272f..aedc0407b42 100644 --- a/idea/src/META-INF/gradle.xml +++ b/idea/src/META-INF/gradle.xml @@ -1,13 +1,14 @@ - - - - - + + + + + @@ -19,9 +20,7 @@ language="Groovy" hasStaticDescription="true" level="WARNING"/> - - + + + + + + diff --git a/idea/src/META-INF/maven.xml b/idea/src/META-INF/maven.xml index 3d38512ed1f..09f2d439b63 100644 --- a/idea/src/META-INF/maven.xml +++ b/idea/src/META-INF/maven.xml @@ -2,9 +2,10 @@ - + + @@ -21,6 +22,12 @@ + + + + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectDataService.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectDataService.kt index c8bdbd16100..66fa8116ed4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectDataService.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinGradleProjectDataService.kt @@ -23,6 +23,8 @@ import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsPr import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.configureFacet import org.jetbrains.kotlin.idea.facet.getOrCreateFacet import org.jetbrains.kotlin.idea.inspections.gradle.findAll @@ -30,6 +32,15 @@ import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +interface GradleProjectImportHandler { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.gradleProjectImportHandler", + GradleProjectImportHandler::class.java + ) + + operator fun invoke(facet: KotlinFacet, sourceSetNode: DataNode) +} + class KotlinGradleProjectDataService : AbstractProjectDataService() { override fun getTargetDataKey() = GradleSourceSetData.KEY @@ -47,7 +58,9 @@ class KotlinGradleProjectDataService : AbstractProjectDataService +private val KOTLIN_PLUGIN_PATH_MARKER = "${KotlinWithGradleConfigurator.GROUP_ID}/${KotlinWithGradleConfigurator.GRADLE_PLUGIN_ID}/" + +// Maven local repo path (example): ~/.m2/repository/org/jetbrains/kotlin/kotlin-runtime/ +private val KOTLIN_PLUGIN_PATH_MARKER_FOR_MAVEN_LOCAL_REPO = + "${KotlinWithGradleConfigurator.GROUP_ID.replace('.', '/')}/${KotlinWithGradleConfigurator.GRADLE_PLUGIN_ID}/" + internal fun findKotlinPluginVersion(classpathData: BuildScriptClasspathData): String? { for (classPathEntry in classpathData.classpathEntries) { for (path in classPathEntry.classesFile) { val uniformedPath = path.replace('\\', '/') + // check / for local maven repo, and '.' for gradle if (uniformedPath.contains(KOTLIN_PLUGIN_PATH_MARKER)) { val versionSubstring = uniformedPath.substringAfter(KOTLIN_PLUGIN_PATH_MARKER).substringBefore('/', "") if (versionSubstring != "") { - return versionSubstring; + return versionSubstring + } + } else if (uniformedPath.contains(KOTLIN_PLUGIN_PATH_MARKER_FOR_MAVEN_LOCAL_REPO)) { + val versionSubstring = uniformedPath.substringAfter(KOTLIN_PLUGIN_PATH_MARKER_FOR_MAVEN_LOCAL_REPO).substringBefore('/', "") + if (versionSubstring != "") { + return versionSubstring } } } diff --git a/libraries/tools/kotlin-allopen/pom.xml b/libraries/tools/kotlin-allopen/pom.xml index 79702108e65..d6d980393fd 100755 --- a/libraries/tools/kotlin-allopen/pom.xml +++ b/libraries/tools/kotlin-allopen/pom.xml @@ -6,7 +6,7 @@ 4.0.0 3.0.4 - ${basedir}/../../../plugins/allopen/src + ${basedir}/../../../plugins/allopen/allopen-cli/src ${basedir}/src/main/kotlin ${basedir}/src/main/resources ${basedir}/target/src/main/kotlin diff --git a/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt b/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt index ab91c24af24..e361a903054 100644 --- a/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt +++ b/libraries/tools/kotlin-allopen/src/main/kotlin/org/jetbrains/kotlin/allopen/gradle/AllOpenSubplugin.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.allopen.gradle import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.internal.AbstractTask import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin @@ -32,14 +34,39 @@ class AllOpenGradleSubplugin : Plugin { } } + fun Project.getBuildscriptArtifacts(): Set = + buildscript.configurations.findByName("classpath")?.resolvedConfiguration?.resolvedArtifacts ?: emptySet() + override fun apply(project: Project) { - project.extensions.create("allOpen", AllOpenExtension::class.java) + val allOpenExtension = project.extensions.create("allOpen", AllOpenExtension::class.java) + + project.afterEvaluate { + val fqNamesAsString = allOpenExtension.myAnnotations.joinToString(",") + project.extensions.extraProperties.set("kotlinAllOpenAnnotations", fqNamesAsString) + + val allBuildscriptArtifacts = project.getBuildscriptArtifacts() + project.rootProject.getBuildscriptArtifacts() + val allOpenCompilerPluginFile = allBuildscriptArtifacts.filter { + val id = it.moduleVersion.id + id.group == AllOpenKotlinGradleSubplugin.ALLOPEN_GROUP_NAME + && id.name == AllOpenKotlinGradleSubplugin.ALLOPEN_ARTIFACT_NAME + }.firstOrNull()?.file?.absolutePath ?: "" + + open class TaskForAllOpen : AbstractTask() + project.tasks.add(project.tasks.create("allOpenDataStorageTask", TaskForAllOpen::class.java).apply { + isEnabled = false + description = "Supported annotations: " + fqNamesAsString + + "; Compiler plugin classpath: $allOpenCompilerPluginFile" + }) + } } } class AllOpenKotlinGradleSubplugin : KotlinGradleSubplugin { - private companion object { - val ANNOTATIONS_ARG_NAME = "annotation" + companion object { + val ALLOPEN_GROUP_NAME = "org.jetbrains.kotlin" + val ALLOPEN_ARTIFACT_NAME = "kotlin-allopen" + + private val ANNOTATIONS_ARG_NAME = "annotation" } override fun isApplicable(project: Project, task: AbstractCompile) = AllOpenGradleSubplugin.isEnabled(project) @@ -64,7 +91,7 @@ class AllOpenKotlinGradleSubplugin : KotlinGradleSubplugin { return options } - override fun getArtifactName() = "kotlin-allopen" - override fun getGroupName() = "org.jetbrains.kotlin" - override fun getPluginName() = "org.jetbrains.kotlin.allopen" + override fun getGroupName() = ALLOPEN_GROUP_NAME + override fun getArtifactName() = ALLOPEN_ARTIFACT_NAME + override fun getCompilerPluginId() = "org.jetbrains.kotlin.allopen" } \ No newline at end of file diff --git a/plugins/allopen/allopen.iml b/plugins/allopen/allopen-cli/allopen-cli.iml similarity index 100% rename from plugins/allopen/allopen.iml rename to plugins/allopen/allopen-cli/allopen-cli.iml diff --git a/plugins/allopen/src/AllOpenDeclarationAttributeAltererExtension.kt b/plugins/allopen/allopen-cli/src/AllOpenDeclarationAttributeAltererExtension.kt similarity index 64% rename from plugins/allopen/src/AllOpenDeclarationAttributeAltererExtension.kt rename to plugins/allopen/allopen-cli/src/AllOpenDeclarationAttributeAltererExtension.kt index fc91915c3ff..1c19c5ccfc2 100644 --- a/plugins/allopen/src/AllOpenDeclarationAttributeAltererExtension.kt +++ b/plugins/allopen/allopen-cli/src/AllOpenDeclarationAttributeAltererExtension.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.allopen import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -28,10 +27,14 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.types.TypeUtils -class AllOpenDeclarationAttributeAltererExtension(val allOpenAnnotationFqNames: List) : DeclarationAttributeAltererExtension { - private companion object { - private val INHERITED_FQNAME = "java.lang.annotation.Inherited" - } +class CliAllOpenDeclarationAttributeAltererExtension( + private val allOpenAnnotationFqNames: List +) : AbstractAllOpenDeclarationAttributeAltererExtension() { + override fun getAllOpenAnnotationFqNames(modifierListOwner: KtModifierListOwner) = allOpenAnnotationFqNames +} + +abstract class AbstractAllOpenDeclarationAttributeAltererExtension : DeclarationAttributeAltererExtension { + abstract fun getAllOpenAnnotationFqNames(modifierListOwner: KtModifierListOwner): List override fun refineDeclarationModality( modifierListOwner: KtModifierListOwner, @@ -40,10 +43,8 @@ class AllOpenDeclarationAttributeAltererExtension(val allOpenAnnotationFqNames: currentModality: Modality, bindingContext: BindingContext ): Modality? { - // We alter only 'final' modality - when (currentModality) { - Modality.OPEN, Modality.ABSTRACT, Modality.SEALED -> return null - else -> {} + if (currentModality != Modality.FINAL) { + return null } // Explicit final @@ -52,31 +53,34 @@ class AllOpenDeclarationAttributeAltererExtension(val allOpenAnnotationFqNames: } val descriptor = declaration ?: containingDeclaration ?: return null - if (descriptor.hasAllOpenAnnotation()) return Modality.OPEN + if (descriptor.hasAllOpenAnnotation(modifierListOwner)) return Modality.OPEN return null } - private fun DeclarationDescriptor.hasAllOpenAnnotation(): Boolean { - if (annotations.any { it.isAllOpenAnnotation() }) return true + private fun DeclarationDescriptor.hasAllOpenAnnotation(modifierListOwner: KtModifierListOwner): Boolean { + if (annotations.any { it.isAllOpenAnnotation(modifierListOwner) }) return true if (this is ClassDescriptor) { for (superType in TypeUtils.getAllSupertypes(defaultType)) { val superTypeDescriptor = superType.constructor.declarationDescriptor as? ClassDescriptor ?: continue - if (superTypeDescriptor.annotations.any { it.isAllOpenAnnotation() }) return true + if (superTypeDescriptor.annotations.any { it.isAllOpenAnnotation(modifierListOwner) }) return true } } return false } - private fun AnnotationDescriptor.isAllOpenAnnotation(allowMetaAnnotations: Boolean = true): Boolean { + private fun AnnotationDescriptor.isAllOpenAnnotation( + modifierListOwner: KtModifierListOwner, + allowMetaAnnotations: Boolean = true + ): Boolean { val annotationType = type.constructor.declarationDescriptor ?: return false - if (annotationType.fqNameSafe.asString() in allOpenAnnotationFqNames) return true + if (annotationType.fqNameSafe.asString() in getAllOpenAnnotationFqNames(modifierListOwner)) return true if (allowMetaAnnotations) { for (metaAnnotation in annotationType.annotations) { - if (metaAnnotation.isAllOpenAnnotation(allowMetaAnnotations = false)) { + if (metaAnnotation.isAllOpenAnnotation(modifierListOwner, allowMetaAnnotations = false)) { return true } } diff --git a/plugins/allopen/src/AllOpenPlugin.kt b/plugins/allopen/allopen-cli/src/AllOpenPlugin.kt similarity index 93% rename from plugins/allopen/src/AllOpenPlugin.kt rename to plugins/allopen/allopen-cli/src/AllOpenPlugin.kt index 688ec5e1bf4..5eae22eb22a 100644 --- a/plugins/allopen/src/AllOpenPlugin.kt +++ b/plugins/allopen/allopen-cli/src/AllOpenPlugin.kt @@ -34,9 +34,11 @@ class AllOpenCommandLineProcessor : CommandLineProcessor { companion object { val ANNOTATION_OPTION = CliOption("annotation", "", "Annotation qualified names", required = false, allowMultipleOccurrences = true) + + val PLUGIN_ID = "org.jetbrains.kotlin.allopen" } - override val pluginId = "org.jetbrains.kotlin.allopen" + override val pluginId = PLUGIN_ID override val pluginOptions = listOf(ANNOTATION_OPTION) override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) = when (option) { @@ -54,6 +56,6 @@ class AllOpenComponentRegistrar : ComponentRegistrar { val annotations = configuration.get(AllOpenConfigurationKeys.ANNOTATION) ?: return if (annotations.isEmpty()) return - DeclarationAttributeAltererExtension.registerExtension(project, AllOpenDeclarationAttributeAltererExtension(annotations)) + DeclarationAttributeAltererExtension.registerExtension(project, CliAllOpenDeclarationAttributeAltererExtension(annotations)) } } \ No newline at end of file diff --git a/plugins/allopen/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor b/plugins/allopen/allopen-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor similarity index 100% rename from plugins/allopen/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor rename to plugins/allopen/allopen-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor diff --git a/plugins/allopen/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/allopen/allopen-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar similarity index 100% rename from plugins/allopen/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar rename to plugins/allopen/allopen-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar diff --git a/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/allOpenOnNotClasses.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/allOpenOnNotClasses.kt diff --git a/plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/allOpenOnNotClasses.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/allOpenOnNotClasses.txt diff --git a/plugins/allopen/testData/bytecodeListing/alreadyOpen.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/alreadyOpen.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/alreadyOpen.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/alreadyOpen.kt diff --git a/plugins/allopen/testData/bytecodeListing/alreadyOpen.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/alreadyOpen.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/alreadyOpen.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/alreadyOpen.txt diff --git a/plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.kt new file mode 100644 index 00000000000..efc2e0b18b2 --- /dev/null +++ b/plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.kt @@ -0,0 +1,18 @@ +annotation class AllOpen + +@AllOpen +final class Test1 + +@AllOpen +class Test2 { + fun method1() {} + val prop1: String = "" + + final fun method2() {} + final val prop2: String = "" + final var prop3: String = "" + + // Modifier 'final' is not applicable to 'setter' + // var prop4: String = "" + // final set +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/explicitFinal.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.txt similarity index 62% rename from plugins/allopen/testData/bytecodeListing/explicitFinal.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.txt index 4d80cc83279..6d540c89207 100644 --- a/plugins/allopen/testData/bytecodeListing/explicitFinal.txt +++ b/plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.txt @@ -13,9 +13,12 @@ public final class Test1 { public class Test2 { private final @org.jetbrains.annotations.NotNull field prop1: java.lang.String private final @org.jetbrains.annotations.NotNull field prop2: java.lang.String + private @org.jetbrains.annotations.NotNull field prop3: java.lang.String public method (): void public @org.jetbrains.annotations.NotNull method getProp1(): java.lang.String - public @org.jetbrains.annotations.NotNull method getProp2(): java.lang.String + public final @org.jetbrains.annotations.NotNull method getProp2(): java.lang.String + public final @org.jetbrains.annotations.NotNull method getProp3(): java.lang.String public method method1(): void public final method method2(): void + public final method setProp3(@org.jetbrains.annotations.NotNull p0: java.lang.String): void } \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/metaAnnotation.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/metaAnnotation.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/metaAnnotation.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/metaAnnotation.kt diff --git a/plugins/allopen/testData/bytecodeListing/metaAnnotation.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/metaAnnotation.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/metaAnnotation.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/metaAnnotation.txt diff --git a/plugins/allopen/testData/bytecodeListing/nestedInner.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/nestedInner.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/nestedInner.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/nestedInner.kt diff --git a/plugins/allopen/testData/bytecodeListing/nestedInner.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/nestedInner.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/nestedInner.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/nestedInner.txt diff --git a/plugins/allopen/testData/bytecodeListing/noAllOpen.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/noAllOpen.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/noAllOpen.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/noAllOpen.kt diff --git a/plugins/allopen/testData/bytecodeListing/noAllOpen.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/noAllOpen.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/noAllOpen.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/noAllOpen.txt diff --git a/plugins/allopen/testData/bytecodeListing/sealed.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/sealed.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/sealed.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/sealed.kt diff --git a/plugins/allopen/testData/bytecodeListing/sealed.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/sealed.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/sealed.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/sealed.txt diff --git a/plugins/allopen/testData/bytecodeListing/severalAllOpen.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/severalAllOpen.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/severalAllOpen.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/severalAllOpen.kt diff --git a/plugins/allopen/testData/bytecodeListing/severalAllOpen.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/severalAllOpen.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/severalAllOpen.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/severalAllOpen.txt diff --git a/plugins/allopen/testData/bytecodeListing/simple.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/simple.kt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/simple.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/simple.kt diff --git a/plugins/allopen/testData/bytecodeListing/simple.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/simple.txt similarity index 100% rename from plugins/allopen/testData/bytecodeListing/simple.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/simple.txt diff --git a/plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt b/plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.kt similarity index 72% rename from plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.kt index 1a632a459ad..e68124c28e5 100644 --- a/plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt +++ b/plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.kt @@ -20,9 +20,9 @@ interface Intf { } open class IntfImpl : Intf { - fun intfImplMethod_ShouldBeFinal() {} + fun intfImplMethod_ShouldBeOpen() {} } -class IntfImpl2_ShouldBeFinalBecauseIntfIsAnInterface : IntfImpl() { - fun intfImpl2Method_ShouldBeFinal() {} +class IntfImpl2_ShouldBeOpen : IntfImpl() { + fun intfImpl2Method_ShouldBeOpen() {} } \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/superClassAnnotation.txt b/plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.txt similarity index 85% rename from plugins/allopen/testData/bytecodeListing/superClassAnnotation.txt rename to plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.txt index 86363218132..13260fd2635 100644 --- a/plugins/allopen/testData/bytecodeListing/superClassAnnotation.txt +++ b/plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.txt @@ -39,12 +39,12 @@ public interface Intf { @kotlin.Metadata public class IntfImpl { public method (): void - public final method intfImplMethod_ShouldBeFinal(): void + public method intfImplMethod_ShouldBeOpen(): void public method intfMethod(): void } @kotlin.Metadata -public final class IntfImpl2_ShouldBeFinalBecauseIntfIsAnInterface { +public class IntfImpl2_ShouldBeOpen { public method (): void - public final method intfImpl2Method_ShouldBeFinal(): void + public method intfImpl2Method_ShouldBeOpen(): void } \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/allopen-ide.iml b/plugins/allopen/allopen-ide/allopen-ide.iml new file mode 100644 index 00000000000..41c5789c7c0 --- /dev/null +++ b/plugins/allopen/allopen-ide/allopen-ide.iml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/src/AllOpenGradleProjectImportHandler.kt b/plugins/allopen/allopen-ide/src/AllOpenGradleProjectImportHandler.kt new file mode 100644 index 00000000000..17f9ab344a2 --- /dev/null +++ b/plugins/allopen/allopen-ide/src/AllOpenGradleProjectImportHandler.kt @@ -0,0 +1,29 @@ +/* + * 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.allopen.ide + +import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor +import org.jetbrains.kotlin.annotation.plugin.ide.AbstractGradleImportHandler +import org.jetbrains.kotlin.utils.PathUtil + +class AllOpenGradleProjectImportHandler : AbstractGradleImportHandler() { + override val compilerPluginId = AllOpenCommandLineProcessor.PLUGIN_ID + override val pluginName = "allopen" + override val annotationOptionName = AllOpenCommandLineProcessor.ANNOTATION_OPTION.name + override val dataStorageTaskName = "allOpenDataStorageTask" + override val pluginJarFileFromIdea = PathUtil.getKotlinPathsForIdeaPlugin().allOpenPluginJarPath +} \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/src/AllOpenMavenProjectImportHandler.kt b/plugins/allopen/allopen-ide/src/AllOpenMavenProjectImportHandler.kt new file mode 100644 index 00000000000..5e78491d4b0 --- /dev/null +++ b/plugins/allopen/allopen-ide/src/AllOpenMavenProjectImportHandler.kt @@ -0,0 +1,56 @@ +/* + * 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.allopen.ide + +import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor +import org.jetbrains.kotlin.annotation.plugin.ide.AbstractMavenImportHandler + +class AllOpenMavenProjectImportHandler : AbstractMavenImportHandler() { + private companion object { + val ANNOTATION_PARAMETER_PREFIX = "all-open:${AllOpenCommandLineProcessor.ANNOTATION_OPTION.name}=" + + private val SPRING_ALLOPEN_ANNOTATIONS = listOf( + "org.springframework.stereotype.Component", + "org.springframework.transaction.annotation.Transactional", + "org.springframework.scheduling.annotation.Async", + "org.springframework.cache.annotation.Cacheable" + ) + } + + override val compilerPluginId = AllOpenCommandLineProcessor.PLUGIN_ID + override val pluginName = "allopen" + override val annotationOptionName = AllOpenCommandLineProcessor.ANNOTATION_OPTION.name + override val mavenPluginArtifactName = "kotlin-maven-allopen" + + override fun getAnnotations(enabledCompilerPlugins: List, compilerPluginOptions: List): List? { + if ("all-open" !in enabledCompilerPlugins && "spring" !in enabledCompilerPlugins) { + return null + } + + val annotations = mutableListOf() + if ("spring" in enabledCompilerPlugins) { + annotations.addAll(SPRING_ALLOPEN_ANNOTATIONS) + } + + annotations.addAll(compilerPluginOptions.mapNotNull { text -> + if (!text.startsWith(ANNOTATION_PARAMETER_PREFIX)) return@mapNotNull null + text.substring(ANNOTATION_PARAMETER_PREFIX.length) + }) + + return annotations + } +} \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt b/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt new file mode 100644 index 00000000000..8f564ae1503 --- /dev/null +++ b/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt @@ -0,0 +1,61 @@ +/* + * 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.allopen.ide + +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.psi.util.CachedValue +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.kotlin.allopen.AbstractAllOpenDeclarationAttributeAltererExtension +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor.Companion.PLUGIN_ID +import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor.Companion.ANNOTATION_OPTION +import java.util.* + +class IdeAllOpenDeclarationAttributeAltererExtension(val project: Project) : AbstractAllOpenDeclarationAttributeAltererExtension() { + private companion object { + val ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.name}=" + } + + private val cache: CachedValue>> = cachedValue(project) { + CachedValueProvider.Result.create(WeakHashMap>(), ProjectRootModificationTracker.getInstance(project)) + } + + override fun getAllOpenAnnotationFqNames(modifierListOwner: KtModifierListOwner): List { + val project = modifierListOwner.project + + val virtualFile = modifierListOwner.containingFile?.virtualFile ?: return emptyList() + val module = ProjectRootManager.getInstance(project).fileIndex.getModuleForFile(virtualFile) ?: return emptyList() + + return cache.value.getOrPut(module) { + val kotlinFacet = KotlinFacet.get(module) ?: return@getOrPut emptyList() + val commonArgs = kotlinFacet.configuration.settings.compilerInfo.commonCompilerArguments ?: return@getOrPut emptyList() + + commonArgs.pluginOptions?.filter { it.startsWith(ANNOTATION_OPTION_PREFIX) } + ?.map { it.substring(ANNOTATION_OPTION_PREFIX.length) } + ?: emptyList() + } + } + + private fun cachedValue(project: Project, result: () -> CachedValueProvider.Result): CachedValue { + return CachedValuesManager.getManager(project).createCachedValue(result, false) + } +} \ No newline at end of file diff --git a/plugins/allopen/testData/bytecodeListing/explicitFinal.kt b/plugins/allopen/testData/bytecodeListing/explicitFinal.kt deleted file mode 100644 index 59f4e90b632..00000000000 --- a/plugins/allopen/testData/bytecodeListing/explicitFinal.kt +++ /dev/null @@ -1,13 +0,0 @@ -annotation class AllOpen - -@AllOpen -final class Test1 - -@AllOpen -class Test2 { - fun method1() {} - val prop1: String = "" - - final fun method2() {} - val prop2: String = "" -} \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/annotation-based-compiler-plugins-ide-support.iml b/plugins/annotation-based-compiler-plugins-ide-support/annotation-based-compiler-plugins-ide-support.iml new file mode 100644 index 00000000000..769bf18c2f0 --- /dev/null +++ b/plugins/annotation-based-compiler-plugins-ide-support/annotation-based-compiler-plugins-ide-support.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractGradleImportHandler.kt b/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractGradleImportHandler.kt new file mode 100644 index 00000000000..8d11d33e37d --- /dev/null +++ b/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractGradleImportHandler.kt @@ -0,0 +1,66 @@ +/* + * 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.plugin.ide + +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.task.TaskData +import org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import java.io.File + +abstract class AbstractGradleImportHandler : GradleProjectImportHandler { + private companion object { + private val TASK_DESCRIPTION_REGEX = "Supported annotations: (.*?); Compiler plugin classpath: (.*)".toRegex() + } + + abstract val compilerPluginId: String + abstract val pluginName: String + abstract val annotationOptionName: String + abstract val dataStorageTaskName: String + abstract val pluginJarFileFromIdea: File + + override fun invoke(facet: KotlinFacet, sourceSetNode: DataNode) { + modifyCompilerArgumentsForPlugin(facet, getPluginSetup(sourceSetNode), + compilerPluginId = compilerPluginId, + pluginName = pluginName, + annotationOptionName = annotationOptionName) + } + + private fun getPluginSetup( + sourceSetNode: DataNode + ): AnnotationBasedCompilerPluginSetup? { + val moduleNode = sourceSetNode.parent ?: return null + if (moduleNode.data !is ModuleData) return null + val dataStorageTaskData = moduleNode.children.firstOrNull { + val data = it.data as? TaskData ?: return@firstOrNull false + data.name == dataStorageTaskName + }?.data as? TaskData ?: return null + + val dataStorageTaskDescription = dataStorageTaskData.description ?: return null + val (annotationFqNamesList, classpathList) = TASK_DESCRIPTION_REGEX.matchEntire( + dataStorageTaskDescription)?.groupValues?.drop(1) ?: return null + + val annotationFqNames = annotationFqNamesList.split(',') + + // For now we can't use plugins from Gradle cause they're shaded. So we use ones from the IDEA plugin + val classpath = listOf(pluginJarFileFromIdea.absolutePath) + + return AnnotationBasedCompilerPluginSetup(annotationFqNames, classpath) + } +} \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractMavenImportHandler.kt b/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractMavenImportHandler.kt new file mode 100644 index 00000000000..93be6d32aab --- /dev/null +++ b/plugins/annotation-based-compiler-plugins-ide-support/src/AbstractMavenImportHandler.kt @@ -0,0 +1,78 @@ +/* + * 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.plugin.ide + +import org.jdom.Element +import org.jdom.Text +import org.jetbrains.idea.maven.project.MavenProject +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.kotlin.idea.maven.MavenProjectImportHandler +import org.jetbrains.kotlin.idea.maven.KotlinMavenImporter.Companion.KOTLIN_PLUGIN_GROUP_ID +import org.jetbrains.kotlin.idea.maven.KotlinMavenImporter.Companion.KOTLIN_PLUGIN_ARTIFACT_ID +import java.io.File + +abstract class AbstractMavenImportHandler : MavenProjectImportHandler { + abstract val compilerPluginId: String + abstract val pluginName: String + abstract val annotationOptionName: String + abstract val mavenPluginArtifactName: String + + override fun invoke(facet: KotlinFacet, mavenProject: MavenProject) { + modifyCompilerArgumentsForPlugin(facet, getPluginSetup(mavenProject), + compilerPluginId = compilerPluginId, + pluginName = pluginName, + annotationOptionName = annotationOptionName) + } + + abstract fun getAnnotations(enabledCompilerPlugins: List, compilerPluginOptions: List): List? + + private fun getPluginSetup(mavenProject: MavenProject): AnnotationBasedCompilerPluginSetup? { + val kotlinPlugin = mavenProject.plugins.firstOrNull { + it.groupId == KOTLIN_PLUGIN_GROUP_ID && it.artifactId == KOTLIN_PLUGIN_ARTIFACT_ID + } ?: return null + + val runtimeJarFile = mavenProject.dependencies + .firstOrNull { it.groupId == KOTLIN_PLUGIN_GROUP_ID && + (it.artifactId == "kotlin-runtime" || it.artifactId == "kotlin-stdlib") } + ?.file ?: return null + val runtimeVersion = runtimeJarFile.parentFile.name + + val mavenCompilerPluginJar = File(runtimeJarFile.parentFile.parentFile.parentFile, + "$mavenPluginArtifactName/$runtimeVersion/$mavenPluginArtifactName-$runtimeVersion.jar") + + val configuration = kotlinPlugin.configurationElement ?: return null + + val enabledCompilerPlugins = configuration.getElement("compilerPlugins") + ?.getElements() + ?.flatMap { plugin -> plugin.content.mapNotNull { (it as? Text)?.text } } + ?: emptyList() + + val compilerPluginOptions = configuration.getElement("pluginOptions") + ?.getElements() + ?.flatMap { it.content } + ?.mapTo(mutableListOf()) { (it as Text).text } + ?: mutableListOf() + + val annotationFqNames = getAnnotations(enabledCompilerPlugins, compilerPluginOptions) ?: return null + return AnnotationBasedCompilerPluginSetup(annotationFqNames, listOf(mavenCompilerPluginJar.absolutePath)) + } + + private fun Element.getElement(name: String) = content.firstOrNull { it is Element && it.name == name } as? Element + + @Suppress("UNCHECKED_CAST") + private fun Element.getElements() = content.filterIsInstance() +} \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt b/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt new file mode 100644 index 00000000000..a2914fc1a16 --- /dev/null +++ b/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt @@ -0,0 +1,52 @@ +/* + * 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.plugin.ide + +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.idea.facet.KotlinFacet + +internal class AnnotationBasedCompilerPluginSetup(val annotationFqNames: List, val classpath: List) + +internal fun modifyCompilerArgumentsForPlugin( + facet: KotlinFacet, + setup: AnnotationBasedCompilerPluginSetup?, + compilerPluginId: String, + pluginName: String, + annotationOptionName: String +) { + val compileInfo = facet.configuration.settings.compilerInfo + + // investigate why copyBean() sometimes throws exceptions + val commonArguments = compileInfo.commonCompilerArguments ?: CommonCompilerArguments.DummyImpl() + + /** See [CommonCompilerArguments.PLUGIN_OPTION_FORMAT] **/ + val annotationOptions = setup?.annotationFqNames?.map { "plugin:$compilerPluginId:$annotationOptionName=$it" } ?: emptyList() + + val oldPluginOptions = (commonArguments.pluginOptions ?: emptyArray()).filterTo(mutableListOf()) { !it.startsWith("plugin:$compilerPluginId:") } + val newPluginOptions = oldPluginOptions + annotationOptions + + val oldPluginClasspaths = (commonArguments.pluginClasspaths ?: emptyArray()).filterTo(mutableListOf()) { + !it.substringAfterLast('/', missingDelimiterValue = "").matches("(kotlin-)?$pluginName-.*\\.jar".toRegex()) + } + + val newPluginClasspaths = oldPluginClasspaths + (setup?.classpath ?: emptyList()) + + commonArguments.pluginOptions = newPluginOptions.toTypedArray() + commonArguments.pluginClasspaths = newPluginClasspaths.toTypedArray() + + compileInfo.commonCompilerArguments = commonArguments +} \ No newline at end of file diff --git a/plugins/plugins-tests/plugins-tests.iml b/plugins/plugins-tests/plugins-tests.iml index a7783d55b6e..a1a09d8797f 100755 --- a/plugins/plugins-tests/plugins-tests.iml +++ b/plugins/plugins-tests/plugins-tests.iml @@ -37,6 +37,6 @@ - + \ No newline at end of file diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt index 2cae74ffb82..bb338b66d21 100644 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/AbstractBytecodeListingTestForAllOpen.kt @@ -25,8 +25,8 @@ abstract class AbstractBytecodeListingTestForAllOpen : AbstractBytecodeListingTe val ALLOPEN_ANNOTATIONS = listOf("AllOpen", "AllOpen2", "test.AllOpen") } - override fun setUpEnvironment(environment: KotlinCoreEnvironment) { + override fun setupEnvironment(environment: KotlinCoreEnvironment) { DeclarationAttributeAltererExtension.registerExtension( - environment.project, AllOpenDeclarationAttributeAltererExtension(ALLOPEN_ANNOTATIONS)) + environment.project, CliAllOpenDeclarationAttributeAltererExtension(ALLOPEN_ANNOTATIONS)) } } \ No newline at end of file diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java index ab84d923a19..46ecff1f568 100644 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/allopen/BytecodeListingTestForAllOpenGenerated.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.allopen; import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.runner.RunWith; @@ -27,71 +28,71 @@ import java.util.regex.Pattern; /** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") -@TestMetadata("plugins/allopen/testData/bytecodeListing") +@TestMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public class BytecodeListingTestForAllOpenGenerated extends AbstractBytecodeListingTestForAllOpen { public void testAllFilesPresentInBytecodeListing() throws Exception { - KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/allopen/testData/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), true); + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/allopen/allopen-cli/testData/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } @TestMetadata("allOpenOnNotClasses.kt") public void testAllOpenOnNotClasses() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/allOpenOnNotClasses.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/allOpenOnNotClasses.kt"); doTest(fileName); } @TestMetadata("alreadyOpen.kt") public void testAlreadyOpen() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/alreadyOpen.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/alreadyOpen.kt"); doTest(fileName); } @TestMetadata("explicitFinal.kt") public void testExplicitFinal() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/explicitFinal.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/explicitFinal.kt"); doTest(fileName); } @TestMetadata("metaAnnotation.kt") public void testMetaAnnotation() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/metaAnnotation.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/metaAnnotation.kt"); doTest(fileName); } @TestMetadata("nestedInner.kt") public void testNestedInner() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/nestedInner.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/nestedInner.kt"); doTest(fileName); } @TestMetadata("noAllOpen.kt") public void testNoAllOpen() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/noAllOpen.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/noAllOpen.kt"); doTest(fileName); } @TestMetadata("sealed.kt") public void testSealed() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/sealed.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/sealed.kt"); doTest(fileName); } @TestMetadata("severalAllOpen.kt") public void testSeveralAllOpen() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/severalAllOpen.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/severalAllOpen.kt"); doTest(fileName); } @TestMetadata("simple.kt") public void testSimple() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/simple.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/simple.kt"); doTest(fileName); } @TestMetadata("superClassAnnotation.kt") public void testSuperClassAnnotation() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/testData/bytecodeListing/superClassAnnotation.kt"); + String fileName = KotlinTestUtils.navigationMetadata("plugins/allopen/allopen-cli/testData/bytecodeListing/superClassAnnotation.kt"); doTest(fileName); } }