diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRule.kt index 255d5cf0b2..266bbbfe1e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.ast.ElementType.BY_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.IMPORT_DIRECTIVE import com.pinterest.ktlint.core.ast.ElementType.OPERATION_REFERENCE @@ -44,9 +45,28 @@ class NoUnusedImportsRule : Rule("no-unused-imports") { // by (https://github.com/shyiko/ktlint/issues/54) "getValue", "setValue" ) + + private val conditionalWhitelist = mapOf Boolean>( + Pair( + // Ignore provideDelegate if there is a `by` anywhere in the file + "org.gradle.kotlin.dsl.provideDelegate", + { rootNode -> + var hasByKeyword = false + rootNode.visit { child -> + if (child.elementType == BY_KEYWORD) { + hasByKeyword = true + return@visit + } + } + hasByKeyword + } + ) + ) + private val ref = mutableSetOf() private val imports = mutableSetOf() private var packageName = "" + private var rootNode: ASTNode? = null override fun visit( node: ASTNode, @@ -54,8 +74,10 @@ class NoUnusedImportsRule : Rule("no-unused-imports") { emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { if (node.isRoot()) { + rootNode = node ref.clear() // rule can potentially be executed more than once (when formatting) ref.add("*") + imports.clear() var parentExpression: String? = null node.visit { vnode -> val psi = vnode.psi @@ -92,7 +114,9 @@ class NoUnusedImportsRule : Rule("no-unused-imports") { importDirective.delete() } } else if (name != null && (!ref.contains(name) || !isAValidImport(importPath)) && - !operatorSet.contains(name) && !name.isComponentN() + !operatorSet.contains(name) && + !name.isComponentN() && + conditionalWhitelist[importPath]?.invoke(rootNode!!) != true ) { emit(node.startOffset, "Unused import", true) if (autoCorrect) { diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRuleTest.kt index d9bf25ba76..d39b914db9 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/NoUnusedImportsRuleTest.kt @@ -395,4 +395,50 @@ class NoUnusedImportsRuleTest { """.trimIndent() ) } + + @Test + fun `provideDelegate is allowed if there is a by keyword`() { + assertThat( + NoUnusedImportsRule().lint( + """ + import org.gradle.api.Plugin + import org.gradle.api.Project + import org.gradle.api.tasks.WriteProperties + import org.gradle.kotlin.dsl.getValue + import org.gradle.kotlin.dsl.provideDelegate + import org.gradle.kotlin.dsl.registering + + class DumpVersionProperties : Plugin { + override fun apply(target: Project) { + with(target) { + val dumpVersionProperties by tasks.registering(WriteProperties::class) { + setProperties(mapOf("version" to "1.2.3")) + outputFile = rootDir.resolve("version.properties") + } + + } + } + } + """.trimIndent() + ) + ).isEmpty() + } + + @Test + fun `provideDelegate is not allowed without by keyword`() { + assertThat( + NoUnusedImportsRule().lint( + """ + import org.gradle.kotlin.dsl.provideDelegate + + fun main() { + } + """.trimIndent() + ) + ).isEqualTo( + listOf( + LintError(1, 1, "no-unused-imports", "Unused import") + ) + ) + } }