Check kotlinx.coroutines libraries used with Kotlin 1.3 in Gradle (KT-25251)

#KT-25251 In Progress
This commit is contained in:
Nikolay Krasko
2018-07-10 16:14:43 +03:00
parent f9de91016a
commit 2a6dfba8bc
8 changed files with 241 additions and 0 deletions
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.inspections.gradle
import com.intellij.codeInspection.CleanupLocalInspectionTool
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.configuration.getWholeModuleGroup
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.versions.LibInfo
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.plugins.gradle.codeInspection.GradleBaseInspection
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression
private val LibInfo.gradleMarker get() = "$groupId:$name:"
private data class DeprecatedForKotlinLibInfo(
val lib: LibInfo,
val sinceKotlinLanguageVersion: LanguageVersion,
val message: String
)
@Suppress("SpellCheckingInspection")
private fun kotlinxCoroutinesDeprecation(name: String): org.jetbrains.kotlin.idea.inspections.gradle.DeprecatedForKotlinLibInfo {
return DeprecatedForKotlinLibInfo(
lib = LibInfo("org.jetbrains.kotlinx", name),
sinceKotlinLanguageVersion = LanguageVersion.KOTLIN_1_3,
message = "Library should be updated to be compatible with Kotlin 1.3"
)
}
@Suppress("SpellCheckingInspection")
private val DEPRECATED_COROUTINES_LIBRARIES_INFORMATION = listOf(
kotlinxCoroutinesDeprecation("kotlinx-coroutines"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-android"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-core"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-core-common"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-core-js"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-guava"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-io"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-javafx"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-jdk8"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-nio"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-quasar"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-reactive"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-reactor"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-rx1"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-rx2"),
kotlinxCoroutinesDeprecation("kotlinx-coroutines-swing")
)
@Suppress("SpellCheckingInspection")
class GradleKotlinxCoroutinesDeprecationInspection : GradleBaseInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(): BaseInspectionVisitor = DependencyFinder()
private open class DependencyFinder : KotlinGradleInspectionVisitor() {
override fun visitClosure(closure: GrClosableBlock) {
super.visitClosure(closure)
val dependenciesCall = closure.getStrictParentOfType<GrMethodCall>() ?: return
if (dependenciesCall.invokedExpression.text != "dependencies") return
val dependencyEntries = GradleHeuristicHelper.findStatementWithPrefixes(closure, PRODUCTION_DEPENDENCY_STATEMENTS)
for (dependencyStatement in dependencyEntries) {
for (outdatedInfo in DEPRECATED_COROUTINES_LIBRARIES_INFORMATION) {
val dependencyText = dependencyStatement.text
val libMarker = outdatedInfo.lib.gradleMarker
if (!dependencyText.contains(libMarker)) continue
if (!checkKotlinVersion(dependencyStatement.containingFile, outdatedInfo.sinceKotlinLanguageVersion)) {
// Same result will be for all invocations in this file, so exit
return
}
val reportOnElement = reportOnElement(dependencyStatement, outdatedInfo)
registerError(
reportOnElement, outdatedInfo.message,
emptyArray(),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
break
}
}
}
private fun reportOnElement(classpathEntry: GrCallExpression, deprecatedForKotlinInfo: DeprecatedForKotlinLibInfo): PsiElement {
val indexOf = classpathEntry.text.indexOf(deprecatedForKotlinInfo.lib.name)
if (indexOf < 0) return classpathEntry
return classpathEntry.findElementAt(indexOf) ?: classpathEntry
}
private fun checkKotlinVersion(file: PsiFile, languageVersion: LanguageVersion): Boolean {
val module = ProjectRootManager.getInstance(file.project).fileIndex.getModuleForFile(file.virtualFile) ?: return false
val moduleGroup = module.getWholeModuleGroup()
return moduleGroup.sourceRootModules.any { moduleInGroup ->
moduleInGroup.languageVersionSettings.languageVersion >= languageVersion
}
}
}
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.inspections.gradle.DeprecatedGradleDependencyInspection
import org.jetbrains.kotlin.idea.inspections.gradle.DifferentKotlinGradleVersionInspection
import org.jetbrains.kotlin.idea.inspections.gradle.DifferentStdlibGradleVersionInspection
import org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection
import org.jetbrains.kotlin.idea.inspections.runInspection
import org.junit.Assert
import org.junit.Test
@@ -343,6 +344,51 @@ class GradleInspectionTest : GradleImportingTestCase() {
)
}
@Test
fun testObsoleteCoroutinesUsage() {
val localFile = createProjectSubFile(
"build.gradle", """
group 'Again'
version '1.0-SNAPSHOT'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0")
}
}
apply plugin: 'kotlin'
repositories {
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
dependencies {
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4'
}
compileKotlin {
kotlinOptions.languageVersion = "1.3"
}
"""
)
importProject()
val tool = GradleKotlinxCoroutinesDeprecationInspection()
val problems = getInspectionResult(tool, localFile)
Assert.assertTrue(problems.size == 1)
Assert.assertEquals(
"Library should be updated to be compatible with Kotlin 1.3",
problems.single()
)
}
fun getInspectionResult(tool: LocalInspectionTool, file: VirtualFile): List<String> {
val resultRef = Ref<List<String>>()
invokeTestRunnable {
@@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.inspections.gradle.DeprecatedGradleDependencyInspection
import org.jetbrains.kotlin.idea.inspections.gradle.DifferentKotlinGradleVersionInspection
import org.jetbrains.kotlin.idea.inspections.gradle.DifferentStdlibGradleVersionInspection
import org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection
import org.jetbrains.kotlin.idea.inspections.runInspection
import org.junit.Assert
import org.junit.Test
@@ -343,6 +344,51 @@ class GradleInspectionTest : GradleImportingTestCase() {
)
}
@Test
fun testObsoleteCoroutinesUsage() {
val localFile = createProjectSubFile(
"build.gradle", """
group 'Again'
version '1.0-SNAPSHOT'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.0")
}
}
apply plugin: 'kotlin'
repositories {
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
dependencies {
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4'
}
compileKotlin {
kotlinOptions.languageVersion = "1.3"
}
"""
)
importProject()
val tool = GradleKotlinxCoroutinesDeprecationInspection()
val problems = getInspectionResult(tool, localFile)
Assert.assertTrue(problems.size == 1)
Assert.assertEquals(
"Library should be updated to be compatible with Kotlin 1.3",
problems.single()
)
}
fun getInspectionResult(tool: LocalInspectionTool, file: VirtualFile): List<String> {
val resultRef = Ref<List<String>>()
invokeTestRunnable {
@@ -0,0 +1,6 @@
<html>
<body>
This inspection reports <b>kotlinx.coroutines</b> libraries dependencies in Gradle that should be updated in order
to be compatible with Kotlin 1.3+.
</body>
</html>
+10
View File
@@ -48,6 +48,16 @@
hasStaticDescription="true"
level="WARNING"/>
<localInspection
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection"
displayName="kotlinx.coroutines dependencies should be updated in Gradle for using with Kotlin 1.3+"
groupPath="Kotlin,Migration"
groupName="Gradle"
enabledByDefault="true"
language="Groovy"
hasStaticDescription="true"
level="ERROR"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>
+10
View File
@@ -56,6 +56,16 @@
hasStaticDescription="true"
level="WARNING"/>
<localInspection
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection"
displayName="kotlinx.coroutines dependencies should be updated in Gradle for using with Kotlin 1.3+"
groupPath="Kotlin,Migration"
groupName="Gradle"
enabledByDefault="true"
language="Groovy"
hasStaticDescription="true"
level="ERROR"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>
+10
View File
@@ -57,6 +57,16 @@
hasStaticDescription="true"
level="WARNING"/>
<localInspection
implementationClass="org.jetbrains.kotlin.idea.inspections.gradle.GradleKotlinxCoroutinesDeprecationInspection"
displayName="kotlinx.coroutines dependencies should be updated in Gradle for using with Kotlin 1.3+"
groupPath="Kotlin,Migration"
groupName="Gradle"
enabledByDefault="true"
language="Groovy"
hasStaticDescription="true"
level="ERROR"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>