Skip to content

Commit

Permalink
Upgrade ktlint to 0.47.1
Browse files Browse the repository at this point in the history
  • Loading branch information
chao2zhang committed Sep 18, 2022
1 parent f5be0d8 commit 9efcbe7
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 117 deletions.
Expand Up @@ -2,8 +2,8 @@ package io.gitlab.arturbosch.detekt.formatting

import com.pinterest.ktlint.core.KtLint
import com.pinterest.ktlint.core.Rule.VisitorModifier.RunAsLateAsPossible
import com.pinterest.ktlint.core.Rule.VisitorModifier.RunOnRootNodeOnly
import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.codeStyleSetProperty
import com.pinterest.ktlint.core.api.EditorConfigProperties
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import io.github.detekt.psi.fileName
import io.github.detekt.psi.toFilePath
Expand All @@ -20,7 +20,8 @@ import io.gitlab.arturbosch.detekt.api.SourceLocation
import io.gitlab.arturbosch.detekt.api.TextLocation
import org.ec4j.core.model.Property
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.lang.FileASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.JavaDummyElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.JavaDummyHolder
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.endOffset

Expand All @@ -38,9 +39,6 @@ abstract class FormattingRule(config: Config) : Rule(config) {
protected val isAndroid
get() = FormattingProvider.android.value(ruleSetConfig)

val runOnRootNodeOnly
get() = RunOnRootNodeOnly in wrapping.visitorModifiers

val runAsLateAsPossible
get() = RunAsLateAsPossible in wrapping.visitorModifiers

Expand All @@ -54,61 +52,86 @@ abstract class FormattingRule(config: Config) : Rule(config) {
this.root = root
positionByOffset = KtLintLineColCalculator
.calculateLineColByOffset(KtLintLineColCalculator.normalizeText(root.text))
root.node.putUserData(KtLint.FILE_PATH_USER_DATA_KEY, root.name)

wrapping.beforeFirstNode(computeEditorConfigProperties())
root.node.visitASTNodes()
wrapping.afterLastNode()
}

open fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String>? = null

val editorConfigProperties = overrideEditorConfigProperties()?.toMutableMap()
open fun getTextLocationForViolation(node: ASTNode, offset: Int) =
TextLocation(node.startOffset, node.psi.endOffset)

private fun computeEditorConfigProperties(): EditorConfigProperties {
val usesEditorConfigProperties = overrideEditorConfigProperties()?.toMutableMap()
?: mutableMapOf()

if (isAndroid) {
editorConfigProperties[codeStyleSetProperty] = "android"
usesEditorConfigProperties[codeStyleSetProperty] = "android"
}

if (editorConfigProperties.isNotEmpty()) {
val userData = (root.node.getUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY).orEmpty())
.toMutableMap()

editorConfigProperties.forEach { (editorConfigProperty, defaultValue) ->
userData[editorConfigProperty.type.name] = Property.builder()
.name(editorConfigProperty.type.name)
.type(editorConfigProperty.type)
.value(defaultValue)
.build()
return buildMap {
usesEditorConfigProperties.forEach { (editorConfigProperty, defaultValue) ->
put(
key = editorConfigProperty.type.name,
value = Property.builder()
.name(editorConfigProperty.type.name)
.type(editorConfigProperty.type)
.value(defaultValue)
.build()
)
}
root.node.putUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY, userData)
}
root.node.putUserData(KtLint.FILE_PATH_USER_DATA_KEY, root.name)
}

open fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String>? = null

fun apply(node: ASTNode) {
if (ruleShouldOnlyRunOnFileNode(node)) {
return
private fun beforeVisitChildNodes(node: ASTNode) {
wrapping.beforeVisitChildNodes(node, autoCorrect) {
offset, errorMessage, canBeAutoCorrected -> node.emit().invoke(offset, errorMessage, canBeAutoCorrected)
}
}

wrapping.visit(node, autoCorrect) { offset, message, _ ->
val (line, column) = positionByOffset(offset)
val location = Location(
SourceLocation(line, column),
getTextLocationForViolation(node, offset),
root.toFilePath()
)

// Nodes reported by 'NoConsecutiveBlankLines' are dangling whitespace nodes which means they have
// no direct parent which we can use to get the containing file needed to baseline or suppress findings.
// For these reasons we do not report a KtElement which may lead to crashes when postprocessing it
// e.g. reports (html), baseline etc.
val packageName = root.packageFqName.asString()
.takeIf { it.isNotEmpty() }
?.plus(".")
.orEmpty()
val entity = Entity("", "$packageName${root.fileName}:$line", location, root)
report(CorrectableCodeSmell(issue, entity, message, autoCorrectEnabled = autoCorrect))
private fun afterVisitChildNodes(node: ASTNode) {
wrapping.afterVisitChildNodes(node, autoCorrect) {
offset, errorMessage, canBeAutoCorrected -> node.emit().invoke(offset, errorMessage, canBeAutoCorrected)
}
}

open fun getTextLocationForViolation(node: ASTNode, offset: Int) =
TextLocation(node.startOffset, node.psi.endOffset)
private fun ASTNode.emit() = { offset: Int, message: String, _: Boolean ->
val (line, column) = positionByOffset(offset)
val location = Location(
SourceLocation(line, column),
getTextLocationForViolation(this, offset),
root.toFilePath()
)

// Nodes reported by 'NoConsecutiveBlankLines' are dangling whitespace nodes which means they have
// no direct parent which we can use to get the containing file needed to baseline or suppress findings.
// For these reasons we do not report a KtElement which may lead to crashes when postprocessing it
// e.g. reports (html), baseline etc.
val packageName = root.packageFqName.asString()
.takeIf { it.isNotEmpty() }
?.plus(".")
.orEmpty()
val entity = Entity("", "$packageName${root.fileName}:$line", location, root)
report(CorrectableCodeSmell(issue, entity, message, autoCorrectEnabled = autoCorrect))
}

private fun ruleShouldOnlyRunOnFileNode(node: ASTNode) =
runOnRootNodeOnly && node !is FileASTNode
private fun ASTNode.visitASTNodes() {
if (isNotDummyElement()) {
beforeVisitChildNodes(this)
}
getChildren(null).forEach {
it.visitASTNodes()
}
if (isNotDummyElement()) {
afterVisitChildNodes(this)
}
}

private fun ASTNode.isNotDummyElement(): Boolean {
val parent = this.psi?.parent
return parent !is JavaDummyHolder && parent !is JavaDummyElement
}
}
Expand Up @@ -57,7 +57,8 @@ import io.gitlab.arturbosch.detekt.formatting.wrappers.SpacingBetweenDeclaration
import io.gitlab.arturbosch.detekt.formatting.wrappers.SpacingBetweenDeclarationsWithComments
import io.gitlab.arturbosch.detekt.formatting.wrappers.SpacingBetweenFunctionNameAndOpeningParenthesis
import io.gitlab.arturbosch.detekt.formatting.wrappers.StringTemplate
import io.gitlab.arturbosch.detekt.formatting.wrappers.TrailingComma
import io.gitlab.arturbosch.detekt.formatting.wrappers.TrailingCommaOnCallSite
import io.gitlab.arturbosch.detekt.formatting.wrappers.TrailingCommaOnDeclarationSite
import io.gitlab.arturbosch.detekt.formatting.wrappers.TypeArgumentListSpacing
import io.gitlab.arturbosch.detekt.formatting.wrappers.TypeParameterListSpacing
import io.gitlab.arturbosch.detekt.formatting.wrappers.UnnecessaryParenthesesBeforeTrailingLambda
Expand Down Expand Up @@ -119,7 +120,8 @@ class KtLintMultiRule(config: Config = Config.empty) :
SpacingBetweenDeclarationsWithAnnotations(config),
SpacingBetweenDeclarationsWithComments(config),
StringTemplate(config),
TrailingComma(config), // in standard ruleset but not enabled by default
TrailingCommaOnCallSite(config), // in standard ruleset but not enabled by default
TrailingCommaOnDeclarationSite(config), // in standard ruleset but not enabled by default
Wrapping(config),

// Wrappers for ktlint-ruleset-experimental rules. Disabled by default.
Expand All @@ -142,43 +144,23 @@ class KtLintMultiRule(config: Config = Config.empty) :
)

override fun visit(root: KtFile) {
val sortedRules = getSortedRules()
sortedRules.forEach { it.visit(root) }
root.node.visitTokens { node ->
sortedRules.forEach { it.apply(node) }
getSortedRules().forEach { rule ->
rule.visit(root)
}
}

internal fun getSortedRules(): List<FormattingRule> {
val runFirstOnRoot = mutableListOf<FormattingRule>()
val other = mutableListOf<FormattingRule>()
val runLastOnRoot = mutableListOf<FormattingRule>()
val runLast = mutableListOf<FormattingRule>()
for (rule in activeRules.filterIsInstance<FormattingRule>()) {
when {
rule.runOnRootNodeOnly && rule.runAsLateAsPossible -> runLastOnRoot.add(rule)
rule.runOnRootNodeOnly -> runFirstOnRoot.add(rule)
rule.runAsLateAsPossible -> runLast.add(rule)
else -> other.add(rule)
}
}
return LinkedList<FormattingRule>().apply {
addAll(runFirstOnRoot)
addAll(other)
addAll(runLastOnRoot)
addAll(runLast)
}
}

private fun ASTNode.visitTokens(currentNode: (ASTNode) -> Unit) {
if (this.isNoFakeElement()) {
currentNode(this)
}
getChildren(null).forEach { it.visitTokens(currentNode) }
}

private fun ASTNode.isNoFakeElement(): Boolean {
val parent = this.psi?.parent
return parent !is JavaDummyHolder && parent !is JavaDummyElement
}
}
@@ -0,0 +1,27 @@
package io.gitlab.arturbosch.detekt.formatting.wrappers

import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.ruleset.standard.TrailingCommaOnCallSiteRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.formatting.FormattingRule

/**
* See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/) for documentation.
*/
@AutoCorrectable(since = "1.23.0")
class TrailingCommaOnCallSite(config: Config) : FormattingRule(config) {

override val wrapping = TrailingCommaOnCallSiteRule()
override val issue = issueFor("Rule to mandate/forbid trailing commas")

@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the call side")
private val allowTrailingComma by config(false)

override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> =
mapOf(
TrailingCommaOnCallSiteRule.allowTrailingCommaOnCallSiteProperty to allowTrailingComma.toString(),
)
}
@@ -1,7 +1,7 @@
package io.gitlab.arturbosch.detekt.formatting.wrappers

import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.ruleset.experimental.trailingcomma.TrailingCommaRule
import com.pinterest.ktlint.ruleset.standard.TrailingCommaOnDeclarationSiteRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable
Expand All @@ -11,21 +11,17 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule
/**
* See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/) for documentation.
*/
@AutoCorrectable(since = "1.20.0")
class TrailingComma(config: Config) : FormattingRule(config) {
@AutoCorrectable(since = "1.23.0")
class TrailingCommaOnDeclarationSite(config: Config) : FormattingRule(config) {

override val wrapping = TrailingCommaRule()
override val wrapping = TrailingCommaOnDeclarationSiteRule()
override val issue = issueFor("Rule to mandate/forbid trailing commas")

@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the defining side")
@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the declaration side")
private val allowTrailingComma by config(false)

@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the calling side")
private val allowTrailingCommaOnCallSite by config(false)

override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> =
mapOf(
TrailingCommaRule.allowTrailingCommaProperty to allowTrailingComma.toString(),
TrailingCommaRule.allowTrailingCommaOnCallSiteProperty to allowTrailingCommaOnCallSite.toString(),
TrailingCommaOnDeclarationSiteRule.allowTrailingCommaProperty to allowTrailingComma.toString(),
)
}
Expand Up @@ -13,14 +13,8 @@ class KtLintMultiRuleSpec {
ktlintRule.visitFile(compileContentForTest(""))
val sortedRules = ktlintRule.getSortedRules()
assertThat(sortedRules).isNotEmpty
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly })
assertThat(sortedRules.indexOfFirst { !it.runAsLateAsPossible })
.isGreaterThan(-1)
.isLessThan(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
assertThat(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
.isGreaterThan(-1)
.isLessThan(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
.isGreaterThan(-1)
.isLessThan(sortedRules.indexOfFirst { it.runAsLateAsPossible && !it.runOnRootNodeOnly })
.isLessThan(sortedRules.indexOfFirst { it.runAsLateAsPossible })
}
}
Expand Up @@ -13,7 +13,6 @@ import java.nio.file.Paths
fun FormattingRule.lint(@Language("kotlin") content: String, fileName: String = "Test.kt"): List<Finding> {
val root = compileContentForTest(content, fileName)
this.visit(root)
root.node.visit { node -> this.apply(node) }
return this.findings
}

Expand Down

0 comments on commit 9efcbe7

Please sign in to comment.