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 19, 2022
1 parent f5be0d8 commit e52a66b
Show file tree
Hide file tree
Showing 25 changed files with 275 additions and 174 deletions.
Expand Up @@ -30,8 +30,11 @@ internal fun PsiElement.searchName(): String {
* Example: KtFile with name /full/path/to/Test.kt will have its name formatted to be simply Test.kt
*/
private fun String.formatElementName(): String =
if (contains(File.separatorChar)) substringAfterLast(File.separatorChar)
else this
if (contains(File.separatorChar)) {
substringAfterLast(File.separatorChar)
} else {
this
}

/*
* KtCompiler wrongly used Path.filename as the name for a KtFile instead of the whole path.
Expand Down
Expand Up @@ -79,15 +79,19 @@ class AnnotationExcluderSpec(private val env: KotlinCoreEnvironment) {
@Test
fun `should exclude when the annotation was found with SplitPattern`() {
val (file, ktAnnotation) = createKtFile("@SinceKotlin")
val excluder = @Suppress("DEPRECATION") AnnotationExcluder(file, SplitPattern("SinceKotlin"))

@Suppress("DEPRECATION")
val excluder = AnnotationExcluder(file, SplitPattern("SinceKotlin"))

assertThat(excluder.shouldExclude(listOf(ktAnnotation))).isTrue()
}

@Test
fun `should exclude when the annotation was found with List of Strings`() {
val (file, ktAnnotation) = createKtFile("@SinceKotlin")
val excluder = @Suppress("DEPRECATION") AnnotationExcluder(file, listOf("SinceKo*"))

@Suppress("DEPRECATION")
val excluder = AnnotationExcluder(file, listOf("SinceKo*"))

assertThat(excluder.shouldExclude(listOf(ktAnnotation))).isTrue()
}
Expand Down
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,12 +39,30 @@ 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

private val emit = { offset: Int, message: String, _: Boolean ->
val (line, column) = positionByOffset(offset)
val location = Location(
SourceLocation(line, column),
// Use offset + 1 since ktlint always reports a single location.
TextLocation(offset, offset + 1),
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 var positionByOffset: (offset: Int) -> Pair<Int, Int> by SingleAssign()
private var root: KtFile by SingleAssign()

Expand All @@ -54,61 +73,66 @@ 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()
}

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

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 ->
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 ->
emit.invoke(offset, errorMessage, canBeAutoCorrected)
}
}

open fun getTextLocationForViolation(node: ASTNode, offset: Int) =
TextLocation(node.startOffset, node.psi.endOffset)
private fun ASTNode.visitASTNodes() {
if (isNotDummyElement()) {
beforeVisitChildNodes(this)
}
getChildren(null).forEach {
it.visitASTNodes()
}
if (isNotDummyElement()) {
afterVisitChildNodes(this)
}
}

private fun ruleShouldOnlyRunOnFileNode(node: ASTNode) =
runOnRootNodeOnly && node !is FileASTNode
private fun ASTNode.isNotDummyElement(): Boolean {
val parent = this.psi?.parent
return parent !is JavaDummyHolder && parent !is JavaDummyElement
}
}
Expand Up @@ -57,16 +57,14 @@ 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
import io.gitlab.arturbosch.detekt.formatting.wrappers.Wrapping
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
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 java.util.LinkedList
import org.jetbrains.kotlin.psi.KtFile

/**
* Runs all KtLint rules.
Expand Down Expand Up @@ -119,7 +117,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 +141,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
}
}
Expand Up @@ -4,13 +4,11 @@ import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.ruleset.standard.IndentationRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.TextLocation
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.formatting.FormattingRule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

/**
* See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#indentation) for documentation.
Expand All @@ -34,15 +32,4 @@ class Indentation(config: Config) : FormattingRule(config) {
mapOf(
DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(),
)

/**
* [IndentationRule] has visitor modifier RunOnRootNodeOnly, so [node] is always the root file.
* Override the parent implementation to highlight the entire file.
*/
override fun getTextLocationForViolation(node: ASTNode, offset: Int): TextLocation {
val relativeEnd = node.text
.drop(offset)
.indexOfFirst { !it.isWhitespace() }
return TextLocation(offset, offset + relativeEnd)
}
}
@@ -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 @@ -4,13 +4,11 @@ import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.ruleset.standard.WrappingRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.TextLocation
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.formatting.FormattingRule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

/**
* See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/#wrapping) for documentation.
Expand All @@ -25,15 +23,6 @@ class Wrapping(config: Config) : FormattingRule(config) {
@Configuration("indentation size")
private val indentSize by config(4)

/**
* [Wrapping] has visitor modifier RunOnRootNodeOnly, so [node] is always the root file.
* Override the parent implementation to highlight the entire file.
*/
override fun getTextLocationForViolation(node: ASTNode, offset: Int): TextLocation {
// Use offset + 1 since Wrapping always reports the location of missing new line.
return TextLocation(offset, offset + 1)
}

override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> =
mapOf(
DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(),
Expand Down

0 comments on commit e52a66b

Please sign in to comment.