From 57aaf0444f2281e129a64f253a33aa29db746e43 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Tue, 6 Mar 2018 16:12:10 +0300 Subject: [PATCH] Add support for "implementation" gradle directive (KT-21746) #KT-21746 Fixed --- .../GradleKotlinFrameworkSupportProvider.kt | 2 +- .../KotlinBuildScriptManipulator.kt | 12 ++-- .../KotlinWithGradleConfigurator.kt | 2 +- .../DeprecatedGradleDependencyInspection.kt | 4 +- .../DifferentStdlibGradleVersionInspection.kt | 18 +---- .../gradle/GradleHeuristicHelper.kt | 10 ++- .../gradle/GradleConfiguratorTest.kt | 33 +++++++++ .../gradle/GradleInspectionTest.kt | 69 +++++++++++++++++++ 8 files changed, 125 insertions(+), 25 deletions(-) diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt index 7b419695b0d..fe6bf060ef9 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleKotlinFrameworkSupportProvider.kt @@ -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( diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt index 402b84531df..14ee9079e47 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt @@ -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) } } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt index 5061780d25b..1300244102e 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt @@ -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'" diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DeprecatedGradleDependencyInspection.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DeprecatedGradleDependencyInspection.kt index 93b92bb4905..7f27edebe06 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DeprecatedGradleDependencyInspection.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DeprecatedGradleDependencyInspection.kt @@ -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() ?: 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) } diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DifferentStdlibGradleVersionInspection.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DifferentStdlibGradleVersionInspection.kt index 7b2d2c1a6a3..023150767ae 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DifferentStdlibGradleVersionInspection.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/DifferentStdlibGradleVersionInspection.kt @@ -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): Collection { val versions = LinkedHashSet() val visitor = object : VersionFinder(libraryId) { @@ -88,18 +85,9 @@ class DifferentStdlibGradleVersionInspection : GradleBaseInspection() { } private fun findLibraryStatement(closure: GrClosableBlock, libraryGroup: String, libraryIds: List): GrCallExpression? { - val applicationStatements = closure.getChildrenOfType() - - for (statement in applicationStatements) { - val startExpression = statement.getChildrenOfType().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? { diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/GradleHeuristicHelper.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/GradleHeuristicHelper.kt index 18320ed753c..90a0995d789 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/GradleHeuristicHelper.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/inspections/gradle/GradleHeuristicHelper.kt @@ -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 { + fun findStatementWithPrefix(closure: GrClosableBlock, prefix: String) = + findStatementWithPrefixes(closure, setOf(prefix)) + + fun findStatementWithPrefixes(closure: GrClosableBlock, prefixes: Set): List { val applicationStatements = closure.getChildrenOfType() val classPathStatements = ArrayList() for (statement in applicationStatements) { val startExpression = statement.getChildrenOfType().firstOrNull() ?: continue - if (prefix == startExpression.text) { + val expressionText = startExpression.text ?: continue + if (expressionText in prefixes) { classPathStatements.add(statement) } } diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt index 296da7c6288..4ab6d49b3a6 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt @@ -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'") diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt index 9fd65d980c6..9ffa9245c03 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt @@ -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 { val resultRef = Ref>() invokeTestRunnable {