-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add custom ktlint rules to prevent Java interop issues (#1414)
(cherry picked from commit 4851cac)
- Loading branch information
Showing
13 changed files
with
333 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Ignore custom ktlint rules for tests | ||
[**/test/**.kt] | ||
disabled_rules = custom-ktlint-rules:top-level-internal,custom-ktlint-rules:top-level-public |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
plugins { | ||
id(Plugins.conventions) | ||
`java-library` | ||
} | ||
|
||
dependencies { | ||
implementation(Deps.ktlint) | ||
|
||
testImplementation(Deps.assertj) | ||
testImplementation(Deps.ktlintTest) | ||
testImplementation(Deps.junitParams) | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} |
10 changes: 10 additions & 0 deletions
10
custom-ktlint-rules/src/main/kotlin/org/partiql/ktlint/CustomRuleSetProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.partiql.ktlint | ||
|
||
import com.pinterest.ktlint.core.RuleSet | ||
import com.pinterest.ktlint.core.RuleSetProvider | ||
import org.partiql.ktlint.rule.TopLevelInternalRule | ||
import org.partiql.ktlint.rule.TopLevelPublicRule | ||
|
||
class CustomRuleSetProvider : RuleSetProvider { | ||
override fun get(): RuleSet = RuleSet("custom-ktlint-rules", TopLevelInternalRule(), TopLevelPublicRule()) | ||
} |
38 changes: 38 additions & 0 deletions
38
custom-ktlint-rules/src/main/kotlin/org/partiql/ktlint/rule/TopLevelInternalRule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package org.partiql.ktlint.rule | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.ast.ElementType | ||
import com.pinterest.ktlint.core.ast.children | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
|
||
public class TopLevelInternalRule : Rule("top-level-internal") { | ||
|
||
override fun visit( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
if (node.elementType != ElementType.IDENTIFIER) { | ||
return | ||
} | ||
|
||
// Focus on just functions and values | ||
val parent = node.treeParent | ||
if (parent.elementType != ElementType.FUN && parent.elementType != ElementType.PROPERTY) { | ||
return | ||
} | ||
|
||
// Check grandparent of node is FILE; if so, is top-level declaration | ||
if (parent.treeParent.elementType != ElementType.FILE) { | ||
return | ||
} | ||
val modifiers = parent.findChildByType(ElementType.MODIFIER_LIST)?.children() | ||
if (modifiers != null && modifiers.any { it.elementType == ElementType.INTERNAL_KEYWORD }) { | ||
emit( | ||
node.startOffset, | ||
"Top-level internal declaration found: ${node.text}", | ||
false | ||
) | ||
} | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
custom-ktlint-rules/src/main/kotlin/org/partiql/ktlint/rule/TopLevelPublicRule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.partiql.ktlint.rule | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.ast.ElementType | ||
import com.pinterest.ktlint.core.ast.children | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
|
||
public class TopLevelPublicRule : Rule("top-level-public") { | ||
override fun visit( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
if (node.elementType != ElementType.IDENTIFIER) { | ||
return | ||
} | ||
|
||
// Focus on just functions and values | ||
val parent = node.treeParent | ||
if (parent.elementType != ElementType.FUN && parent.elementType != ElementType.PROPERTY) { | ||
return | ||
} | ||
|
||
// Check grandparent of node is FILE; if so, is top-level declaration | ||
val grandParent = parent.treeParent | ||
if (grandParent.elementType != ElementType.FILE) { | ||
return | ||
} | ||
|
||
val modifiers = parent.findChildByType(ElementType.MODIFIER_LIST) | ||
if (modifiers != null && modifiers.isNotPublic()) { | ||
return | ||
} | ||
|
||
val annotationEntry = grandParent.findChildByType(ElementType.FILE_ANNOTATION_LIST)?.findChildByType(ElementType.ANNOTATION_ENTRY) | ||
if (annotationEntry == null || !annotationEntry.containsFileJvmName()) { | ||
emit( | ||
node.startOffset, | ||
"Top-level public declaration found without `@file:JvmName` annotation: ${node.text}", | ||
false | ||
) | ||
} | ||
} | ||
|
||
// returns true iff modifiers is not one of `PRIVATE_KEYWORD`, `INTERNAL_KEYWORD` or `PROTECTED_KEYWORD` | ||
private fun ASTNode.isNotPublic(): Boolean { | ||
val modifiers = this.children().map { it.elementType } | ||
return modifiers.any { it == ElementType.PRIVATE_KEYWORD || it == ElementType.INTERNAL_KEYWORD || it == ElementType.PROTECTED_KEYWORD } | ||
} | ||
|
||
// returns true iff node is `@file:JvmName(<some name>)` | ||
private fun ASTNode.containsFileJvmName(): Boolean { | ||
val annotationTarget = this.findChildByType(ElementType.ANNOTATION_TARGET) | ||
if (annotationTarget == null || annotationTarget.text.lowercase() != "file") { | ||
return false | ||
} | ||
val constructorCallee = this.findChildByType(ElementType.CONSTRUCTOR_CALLEE) | ||
if (constructorCallee == null || constructorCallee.text.lowercase() != "jvmname") { | ||
return false | ||
} | ||
return true | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
...lint-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
org.partiql.ktlint.CustomRuleSetProvider |
48 changes: 48 additions & 0 deletions
48
custom-ktlint-rules/src/test/kotlin/org/partiql/ktlint/rule/TopLevelInternalRuleTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.partiql.ktlint.rule | ||
|
||
import com.pinterest.ktlint.core.LintError | ||
import com.pinterest.ktlint.test.lint | ||
import org.assertj.core.api.Assertions | ||
import org.junit.jupiter.api.Test | ||
|
||
class TopLevelInternalRuleTest { | ||
@Test | ||
fun `top-level internal`() { | ||
val code = | ||
""" | ||
internal fun internalTopLevelFun() {} // ktlint error | ||
internal val internalTopLevelVal = 123 // ktlint error | ||
internal var internalTopLevelVar = 456 // ktlint error | ||
// No errors for below (for this rule) | ||
public fun publicTopLevelFun() {} | ||
public val publicTopLevelVal = 123 | ||
public var publicTopLevelVar = 456 | ||
fun publicTopLevelFun2() {} | ||
val publicTopLevelVal = 123 | ||
var publicTopLevelVar = 456 | ||
public class PublicClass { | ||
internal fun internalFun() {} | ||
internal val internalVal = 123 | ||
public fun publicFun() {} | ||
public val publicVal = 123 | ||
} | ||
""".trimIndent() | ||
Assertions.assertThat(TopLevelInternalRule().lint(code)).containsExactly( | ||
LintError(1, 14, "top-level-internal", "Top-level internal declaration found: internalTopLevelFun"), | ||
LintError(3, 14, "top-level-internal", "Top-level internal declaration found: internalTopLevelVal"), | ||
LintError(5, 14, "top-level-internal", "Top-level internal declaration found: internalTopLevelVar") | ||
) | ||
} | ||
} |
Oops, something went wrong.