Add support for "implementation" gradle directive (KT-21746)

#KT-21746 Fixed
This commit is contained in:
Nikolay Krasko
2018-03-06 16:12:10 +03:00
parent 9004cdfa45
commit 57aaf0444f
8 changed files with 125 additions and 25 deletions
@@ -89,7 +89,7 @@ abstract class GradleKotlinFrameworkSupportProvider(
.addBuildscriptPropertyDefinition("ext.kotlin_version = '$kotlinVersion'")
for (dependency in getDependencies(sdk)) {
buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency))
buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "compile"))
}
for (dependency in getTestDependencies()) {
buildScriptData.addDependencyNotation(
@@ -20,6 +20,7 @@ import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
@@ -105,10 +106,10 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
override fun getKotlinStdlibVersion(): String? = kotlinScript.getKotlinStdlibVersion()
private fun KtBlockExpression.addCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? =
findCompileStdLib() ?: addExpressionIfMissing(getCompileDependencySnippet(KOTLIN_GROUP_ID, stdlibArtifactName)) as? KtCallExpression
findStdLibDependency() ?: addExpressionIfMissing(getCompileDependencySnippet(KOTLIN_GROUP_ID, stdlibArtifactName)) as? KtCallExpression
private fun KtFile.containsCompileStdLib(): Boolean =
findScriptInitializer("dependencies")?.getBlock()?.findCompileStdLib() != null
findScriptInitializer("dependencies")?.getBlock()?.findStdLibDependency() != null
private fun KtFile.containsApplyKotlinPlugin(pluginName: String): Boolean =
findScriptInitializer("apply")?.getBlock()?.findPlugin(pluginName) != null
@@ -137,7 +138,7 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
private fun KtFile.getKotlinStdlibVersion(): String? {
return findScriptInitializer("dependencies")?.getBlock()?.let {
val expression = it.findCompileStdLib()?.valueArguments?.firstOrNull()?.getArgumentExpression()
val expression = it.findStdLibDependency()?.valueArguments?.firstOrNull()?.getArgumentExpression()
when (expression) {
is KtCallExpression -> expression.valueArguments[1].text.trim('\"')
is KtStringTemplateExpression -> expression.text?.trim('\"')?.substringAfterLast(":")?.removePrefix("$")
@@ -146,9 +147,10 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
}
}
private fun KtBlockExpression.findCompileStdLib(): KtCallExpression? {
private fun KtBlockExpression.findStdLibDependency(): KtCallExpression? {
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
it.calleeExpression?.text == "compile" && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib() ?: false)
val calleeText = it.calleeExpression?.text
calleeText in PRODUCTION_DEPENDENCY_STATEMENTS && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib() ?: false)
}
}
@@ -264,7 +264,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
fun getGroovyDependencySnippet(artifactName: String, scope: String = "compile") =
fun getGroovyDependencySnippet(artifactName: String, scope: String) =
"$scope \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
@@ -25,6 +25,7 @@ import com.intellij.util.text.VersionComparatorUtil
import org.jetbrains.kotlin.idea.configuration.allModules
import org.jetbrains.kotlin.idea.configuration.getWholeModuleGroup
import org.jetbrains.kotlin.idea.inspections.ReplaceStringInDocumentFix
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
import org.jetbrains.kotlin.idea.versions.DEPRECATED_LIBRARIES_INFORMATION
import org.jetbrains.kotlin.idea.versions.DeprecatedLibInfo
import org.jetbrains.kotlin.idea.versions.LibInfo
@@ -47,7 +48,8 @@ class DeprecatedGradleDependencyInspection : GradleBaseInspection() {
val dependenciesCall = closure.getStrictParentOfType<GrMethodCall>() ?: return
if (dependenciesCall.invokedExpression.text != "dependencies") return
val dependencyEntries = GradleHeuristicHelper.findStatementWithPrefix(closure, "compile")
val dependencyEntries = GradleHeuristicHelper.findStatementWithPrefixes(
closure, PRODUCTION_DEPENDENCY_STATEMENTS)
for (dependencyStatement in dependencyEntries) {
visitDependencyEntry(dependencyStatement)
}
@@ -20,8 +20,8 @@ import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
import org.jetbrains.kotlin.idea.versions.*
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.plugins.gradle.codeInspection.GradleBaseInspection
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor
@@ -29,7 +29,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement
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.GrReferenceExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression
import java.util.*
@@ -70,8 +69,6 @@ class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
}
companion object {
val COMPILE_DEPENDENCY_STATEMENTS = listOf("classpath", "compile")
fun getKotlinStdlibVersions(gradleFile: GroovyFileBase, libraryId: List<String>): Collection<String> {
val versions = LinkedHashSet<String>()
val visitor = object : VersionFinder(libraryId) {
@@ -88,18 +85,9 @@ class DifferentStdlibGradleVersionInspection : GradleBaseInspection() {
}
private fun findLibraryStatement(closure: GrClosableBlock, libraryGroup: String, libraryIds: List<String>): GrCallExpression? {
val applicationStatements = closure.getChildrenOfType<GrCallExpression>()
for (statement in applicationStatements) {
val startExpression = statement.getChildrenOfType<GrReferenceExpression>().firstOrNull() ?: continue
if (startExpression.text in COMPILE_DEPENDENCY_STATEMENTS) {
if (libraryIds.any { it in statement.text } && statement.text.contains(libraryGroup)) {
return statement
}
}
return GradleHeuristicHelper.findStatementWithPrefixes(closure, PRODUCTION_DEPENDENCY_STATEMENTS).firstOrNull { statement ->
libraryIds.any { it in statement.text } && statement.text.contains(libraryGroup)
}
return null
}
fun getResolvedKotlinStdlibVersion(file: PsiFile, libraryIds: List<String>): String? {
@@ -30,6 +30,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrC
import java.util.*
object GradleHeuristicHelper {
val PRODUCTION_DEPENDENCY_STATEMENTS = setOf("classpath", "compile", "api", "implementation", "compileOnly", "runtimeOnly")
fun getHeuristicVersionInBuildScriptDependency(classpathStatement: GrCallExpression): String? {
val argumentList = when (classpathStatement) {
is GrMethodCall -> classpathStatement.argumentList // classpath('argument')
@@ -83,14 +85,18 @@ object GradleHeuristicHelper {
return null
}
fun findStatementWithPrefix(closure: GrClosableBlock, prefix: String): List<GrCallExpression> {
fun findStatementWithPrefix(closure: GrClosableBlock, prefix: String) =
findStatementWithPrefixes(closure, setOf(prefix))
fun findStatementWithPrefixes(closure: GrClosableBlock, prefixes: Set<String>): List<GrCallExpression> {
val applicationStatements = closure.getChildrenOfType<GrCallExpression>()
val classPathStatements = ArrayList<GrCallExpression>()
for (statement in applicationStatements) {
val startExpression = statement.getChildrenOfType<GrReferenceExpression>().firstOrNull() ?: continue
if (prefix == startExpression.text) {
val expressionText = startExpression.text ?: continue
if (expressionText in prefixes) {
classPathStatements.add(statement)
}
}
@@ -254,6 +254,39 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
}
}
@Test
fun testListNonConfiguredModules_ConfiguredWithImplementation() {
createProjectSubFile("settings.gradle", "include ':app'")
createProjectSubFile(
"app/build.gradle", """
buildscript {
repositories {
jcenter()
mavenCentral()
}
}
apply plugin: 'java'
repositories {
jcenter()
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.1.3"
}
""".trimIndent()
)
createProjectSubFile("app/src/main/java/foo.kt", "")
importProject()
runReadAction {
assertEmpty(getCanBeConfiguredModulesWithKotlinFiles(myProject))
}
}
@Test
fun testListNonConfiguredModules_ConfiguredOnlyTest() {
createProjectSubFile("settings.gradle", "include ':app'")
@@ -61,6 +61,39 @@ class GradleInspectionTest : GradleImportingTestCase() {
Assert.assertEquals("Plugin version (1.0.2) is not the same as library version (1.0.3)", problems.single())
}
@Test
fun testDifferentStdlibGradleVersionWithImplementation() {
val localFile = createProjectSubFile(
"build.gradle", """
group 'Again'
version '1.0-SNAPSHOT'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.2")
}
}
apply plugin: 'kotlin'
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.0.3"
}
"""
)
importProject()
val tool = DifferentStdlibGradleVersionInspection()
val problems = getInspectionResult(tool, localFile)
Assert.assertTrue(problems.size == 1)
Assert.assertEquals("Plugin version (1.0.2) is not the same as library version (1.0.3)", problems.single())
}
@Test
fun testDifferentStdlibJre7GradleVersion() {
val localFile = createProjectSubFile(
@@ -274,6 +307,42 @@ class GradleInspectionTest : GradleImportingTestCase() {
)
}
@Test
fun testJreIsDeprecatedWithImplementation() {
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'
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0"
}
"""
)
importProject()
val tool = DeprecatedGradleDependencyInspection()
val problems = getInspectionResult(tool, localFile)
Assert.assertTrue(problems.size == 1)
Assert.assertEquals(
"kotlin-stdlib-jre7 is deprecated since 1.2.0 and should be replaced with kotlin-stdlib-jdk7",
problems.single()
)
}
fun getInspectionResult(tool: LocalInspectionTool, file: VirtualFile): List<String> {
val resultRef = Ref<List<String>>()
invokeTestRunnable {