New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade ktlint to 0.47.1 #5312
Upgrade ktlint to 0.47.1 #5312
Changes from all commits
79281e7
689b9f8
65b1d9c
d058297
cf3bed5
044b86b
c141cfa
a6d3a1f
14d3f2b
4c40352
60791ad
68d4159
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These seems like a false positive from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notes taken: I will investigate this later and open an issue for ktlint. |
||
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() | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -21,9 +21,9 @@ 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 | ||
|
||
/** | ||
* Rule to detect formatting violations. | ||
|
@@ -39,12 +39,35 @@ 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) | ||
|
||
if (canBeCorrectedByKtLint(message)) { | ||
report(CorrectableCodeSmell(issue, entity, message, autoCorrectEnabled = autoCorrect)) | ||
} else { | ||
report(CodeSmell(issue, entity, message)) | ||
} | ||
} | ||
|
||
private var positionByOffset: (offset: Int) -> Pair<Int, Int> by SingleAssign() | ||
private var root: KtFile by SingleAssign() | ||
|
||
|
@@ -58,66 +81,63 @@ 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) | ||
|
||
val editorConfigProperties = overrideEditorConfigProperties()?.toMutableMap() | ||
wrapping.beforeFirstNode(computeEditorConfigProperties()) | ||
root.node.visitASTNodes() | ||
wrapping.afterLastNode() | ||
Comment on lines
+86
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See |
||
} | ||
|
||
open fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String>? = null | ||
|
||
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) | ||
|
||
if (canBeCorrectedByKtLint(message)) { | ||
report(CorrectableCodeSmell(issue, entity, message, autoCorrectEnabled = autoCorrect)) | ||
} else { | ||
report(CodeSmell(issue, entity, message)) | ||
} | ||
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) | ||
Comment on lines
-118
to
-119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is removed because no rules in Ktlint is |
||
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function is renamed to improve readability and consistency |
||
val parent = this.psi?.parent | ||
return parent !is JavaDummyHolder && parent !is JavaDummyElement | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,14 +57,12 @@ 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 | ||
|
||
|
@@ -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 | ||
Comment on lines
+120
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I would propose to keep them disabled because it may sparkle endless conversation within a medium-large sized team There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't they be enforced by default on declaration site in line with the Kotlin coding conventions? https://kotlinlang.org/docs/coding-conventions.html#trailing-commas
I also think they should be enabled by default for the call site for the reasons outlined in the coding conventions:
It's easy to disable for teams that don't want this. I don't think there's a strong reason to deviate from ktlint's default behaviour - and anyone using ktlint directly would have to have the same conversation if they don't like them. It's also easy to disable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Agree on being consistent with KtLint's behavior There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me keep this disabled for now in a separate PR because it will cause at least 200+ violations across the codebase - I will create a separate PR to demonstrate what it looks like. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #5386 is the PR to be consistent with ktlint Shouldn't they be enforced by default on declaration site in line with the Kotlin coding conventions?
|
||
Wrapping(config), | ||
|
||
// Wrappers for ktlint-ruleset-experimental rules. Disabled by default. | ||
|
@@ -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) | ||
Comment on lines
+144
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am trying to move more logic into |
||
} | ||
} | ||
|
||
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 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.22.0") | ||
class TrailingCommaOnCallSite(config: Config) : FormattingRule(config) { | ||
|
||
override val wrapping = TrailingCommaOnCallSiteRule() | ||
override val issue = issueFor("Rule to mandate/forbid trailing commas at call sites") | ||
|
||
@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced at call sites") | ||
private val useTrailingCommaOnCallSite by config(false) | ||
|
||
override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> = | ||
mapOf( | ||
TrailingCommaOnCallSiteRule.allowTrailingCommaOnCallSiteProperty to useTrailingCommaOnCallSite.toString(), | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This violation is detected once pinterest/ktlint#828 is addressed.