From 7f8984c4be4031ca07b33fc7bc8a7a82511b1693 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Sun, 8 Dec 2024 12:03:14 -0500 Subject: [PATCH 01/27] implement ASTNode.isPartOfComment without psi --- .../ktlint/rule/engine/core/api/ASTNodeExtension.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 0eccbd4769..508c8b8760 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -8,7 +8,6 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VARARG_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.VAR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement @@ -223,7 +222,11 @@ public fun ASTNode.isLeaf(): Boolean = firstChildNode == null */ public fun ASTNode.isCodeLeaf(): Boolean = isLeaf() && !isWhiteSpace() && !isPartOfComment() -public fun ASTNode.isPartOfComment(): Boolean = parent(strict = false) { it.psi is PsiComment } != null +public fun ASTNode.isPartOfComment(): Boolean = + parent(strict = false) { + val eType = it.elementType + eType == ElementType.BLOCK_COMMENT || eType == ElementType.EOL_COMMENT || eType == ElementType.KDOC + } != null public fun ASTNode.children(): Sequence = generateSequence(firstChildNode) { node -> node.treeNext } From 256235433631067a02b2ebd9b7df786ef0883718 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 21:13:54 -0500 Subject: [PATCH 02/27] remove psi from existingSuppressionsFromNamedArgumentOrNull --- .../api/ktlint-rule-engine-core.api | 1 + .../rule/engine/core/api/ASTNodeExtension.kt | 8 ++++++++ .../rule/engine/internal/KtlintSuppression.kt | 18 ++++++++++-------- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index a1a00a5a7d..e271d90454 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -56,6 +56,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static synthetic fun prevLeaf$default (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZILjava/lang/Object;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun prevSibling (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static synthetic fun prevSibling$default (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; + public static final fun recursiveChildren (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lkotlin/sequences/Sequence; public static final fun remove (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)V public static final fun replaceWith (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)V public static final fun upsertWhitespaceAfterMe (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Ljava/lang/String;)V diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 508c8b8760..dd79f79a9b 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -230,6 +230,14 @@ public fun ASTNode.isPartOfComment(): Boolean = public fun ASTNode.children(): Sequence = generateSequence(firstChildNode) { node -> node.treeNext } +public fun ASTNode.recursiveChildren(): Sequence = + sequence { + children().forEach { + yield(it) + yieldAll(it.recursiveChildren()) + } + } + /** * Updates or inserts a new whitespace element with [text] before the given node. If the node itself is a whitespace * then its contents is replaced with [text]. If the node is a (nested) composite element, the whitespace element is diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt index 0db38f466e..01e5309ff2 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt @@ -17,6 +17,7 @@ import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isRoot import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling +import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.core.api.replaceWith import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiElement @@ -30,7 +31,6 @@ import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassInitializer -import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclarationModifierList import org.jetbrains.kotlin.psi.KtExpression @@ -47,7 +47,6 @@ import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScriptInitializer import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.psiUtil.children -import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.util.prefixIfNot @@ -202,12 +201,15 @@ private fun ASTNode.existingSuppressions() = existingSuppressionsFromNamedArgumentOrNull() ?: getValueArguments() -private fun ASTNode.existingSuppressionsFromNamedArgumentOrNull() = - psi - .findDescendantOfType() - ?.children - ?.map { it.text } - ?.toSet() +private fun ASTNode.existingSuppressionsFromNamedArgumentOrNull(): Set? = + recursiveChildren() + .firstOrNull { it.elementType == ElementType.COLLECTION_LITERAL_EXPRESSION } + ?.run { + children() + .filter { it.elementType == ElementType.STRING_TEMPLATE } + .map { it.text } + .toSet() + } private fun ASTNode.findSuppressionAnnotations(): Map = if (this.isRoot()) { From a05595fb5fc60c650230b3bc29ed23d47e3742e0 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 21:36:00 -0500 Subject: [PATCH 03/27] remove psi from visitKtlintSuppressionInAnnotation --- .../api/ktlint-rule-engine-core.api | 3 ++- .../rule/engine/core/api/ASTNodeExtension.kt | 5 ++++- .../internal/rules/KtlintSuppressionRule.kt | 15 +++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index e271d90454..24d4d0431e 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -56,7 +56,8 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static synthetic fun prevLeaf$default (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZILjava/lang/Object;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun prevSibling (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static synthetic fun prevSibling$default (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; - public static final fun recursiveChildren (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lkotlin/sequences/Sequence; + public static final fun recursiveChildren (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Z)Lkotlin/sequences/Sequence; + public static synthetic fun recursiveChildren$default (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZILjava/lang/Object;)Lkotlin/sequences/Sequence; public static final fun remove (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)V public static final fun replaceWith (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)V public static final fun upsertWhitespaceAfterMe (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Ljava/lang/String;)V diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index dd79f79a9b..e304fbf6bd 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -230,8 +230,11 @@ public fun ASTNode.isPartOfComment(): Boolean = public fun ASTNode.children(): Sequence = generateSequence(firstChildNode) { node -> node.treeNext } -public fun ASTNode.recursiveChildren(): Sequence = +public fun ASTNode.recursiveChildren(includeSelf: Boolean = false): Sequence = sequence { + if (includeSelf) { + yield(this@recursiveChildren) + } children().forEach { yield(it) yieldAll(it.recursiveChildren()) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt index eba445d21c..b08d3a0ecd 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt @@ -17,6 +17,7 @@ import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.parent import com.pinterest.ktlint.rule.engine.core.api.prevLeaf +import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.core.api.remove import com.pinterest.ktlint.rule.engine.core.api.replaceWith import com.pinterest.ktlint.rule.engine.internal.KTLINT_SUPPRESSION_ID_ALL_RULES @@ -42,9 +43,9 @@ import org.jetbrains.kotlin.psi.KtScriptInitializer import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.KtValueArgument import org.jetbrains.kotlin.psi.KtValueArgumentList -import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.utils.addToStdlib.applyIf /** @@ -105,10 +106,10 @@ public class KtlintSuppressionRule( emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node - .psi - .findDescendantOfType() - ?.node - ?.let { literalStringTemplateEntry -> + .recursiveChildren(includeSelf = true) + .firstOrNull { + it.elementType == ElementType.LITERAL_STRING_TEMPLATE_ENTRY + }?.let { literalStringTemplateEntry -> val prefixedSuppression = literalStringTemplateEntry .text @@ -123,7 +124,9 @@ public class KtlintSuppressionRule( .ifAutocorrectAllowed { node .createLiteralStringTemplateEntry(prefixedSuppression) - ?.let { literalStringTemplateEntry.replaceWith(it) } + ?.let { + literalStringTemplateEntry.replaceWith(it) + } } } } From 1b76046ea71f21816ecc0203495815e53aa2799e Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 21:54:07 -0500 Subject: [PATCH 04/27] remove psi from getAnnotationUseSiteTarget --- .../engine/internal/rules/KtlintSuppressionRule.kt | 4 +--- .../ktlint/ruleset/standard/rules/AnnotationRule.kt | 13 ++++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt index b08d3a0ecd..2ec4de1044 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt @@ -124,9 +124,7 @@ public class KtlintSuppressionRule( .ifAutocorrectAllowed { node .createLiteralStringTemplateEntry(prefixedSuppression) - ?.let { - literalStringTemplateEntry.replaceWith(it) - } + ?.let { literalStringTemplateEntry.replaceWith(it) } } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt index fcb611475c..4376a518c6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -43,11 +44,9 @@ import com.pinterest.ktlint.rule.engine.core.api.prevCodeSibling import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe -import com.pinterest.ktlint.rule.engine.core.util.safeAs import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget -import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.utils.addToStdlib.ifTrue @@ -346,11 +345,10 @@ public class AnnotationRule : private fun ASTNode.isNotReceiverTargetAnnotation() = getAnnotationUseSiteTarget() != AnnotationUseSiteTarget.RECEIVER - private fun ASTNode.getAnnotationUseSiteTarget() = - psi - .safeAs() - ?.useSiteTarget - ?.getAnnotationUseSiteTarget() + private fun ASTNode.getAnnotationUseSiteTarget(): AnnotationUseSiteTarget? = + takeIf { it.elementType == ElementType.ANNOTATION_ENTRY } + ?.findChildByType(ElementType.ANNOTATION_TARGET) + ?.let { USE_SITE_TARGETS[it.text] } private fun ASTNode.isAnnotationEntryWithValueArgumentList() = getAnnotationEntryValueArgumentList() != null @@ -469,6 +467,7 @@ public class AnnotationRule : FILE_ANNOTATION_LIST, MODIFIER_LIST, ) + val USE_SITE_TARGETS = AnnotationUseSiteTarget.entries.associate { it.renderName to it } } } From 546dfca1cc705aa2339ef82bba6a06aa23527353 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 22:22:43 -0500 Subject: [PATCH 05/27] remove psi from isOnSameLineAsControlFlowKeyword --- .../rules/ArgumentListWrappingRule.kt | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt index fa4d840e6f..515fc145f8 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt @@ -1,10 +1,10 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLLECTION_LITERAL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL import com.pinterest.ktlint.rule.engine.core.api.ElementType.LPAR @@ -38,12 +38,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl -import org.jetbrains.kotlin.psi.KtContainerNode -import org.jetbrains.kotlin.psi.KtDoWhileExpression -import org.jetbrains.kotlin.psi.KtIfExpression -import org.jetbrains.kotlin.psi.KtWhileExpression import org.jetbrains.kotlin.psi.psiUtil.children -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType /** * https://kotlinlang.org/docs/reference/coding-conventions.html#method-call-formatting @@ -288,24 +283,19 @@ public class ArgumentListWrappingRule : } private fun ASTNode.isOnSameLineAsControlFlowKeyword(): Boolean { - val containerNode = psi.getStrictParentOfType() ?: return false - if (containerNode.node.elementType == ELSE) return false - val controlFlowKeyword = - when (val parent = containerNode.parent) { - is KtIfExpression -> parent.ifKeyword.node - is KtWhileExpression -> parent.firstChild.node - is KtDoWhileExpression -> parent.whileKeyword?.node - else -> null - } ?: return false - var prevLeaf = prevLeaf() ?: return false - while (prevLeaf != controlFlowKeyword) { + while (!prevLeaf.isControlFlowKeyword) { if (prevLeaf.isWhiteSpaceWithNewline()) return false prevLeaf = prevLeaf.prevLeaf() ?: return false } return true } + private val ASTNode.isControlFlowKeyword: Boolean get() = + elementType == ElementType.IF_KEYWORD || + elementType == ElementType.WHILE_KEYWORD || + elementType == ElementType.DO_KEYWORD + public companion object { private const val UNSET_IGNORE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY = Int.MAX_VALUE public val IGNORE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY: EditorConfigProperty = From 5f0f4a8b790cac9122030b8cdb3dc9a50144a436 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 23:14:00 -0500 Subject: [PATCH 06/27] reduce psi in NoUnusedImportsRule --- .../standard/rules/NoUnusedImportsRule.kt | 79 +++++++++++++++---- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt index 66cded850f..8c957255d2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BY_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE @@ -23,15 +24,13 @@ import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.prevSibling import com.pinterest.ktlint.rule.engine.core.api.remove -import com.pinterest.ktlint.rule.engine.core.util.safeAs import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement +import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.MARKDOWN_LINK -import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink -import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtPackageDirective @@ -85,14 +84,20 @@ public class NoUnusedImportsRule : } MARKDOWN_LINK -> { - node - .psi - .safeAs() - ?.let { kdocLink -> - val linkText = kdocLink.getLinkText().removeBackticksAndTrim() - ref.add(Reference(linkText.split('.').first(), false)) - ref.add(Reference(linkText.split('.').last(), false)) + // Both of these are copied from from org.jetbrains.kotlin.kdoc.psi.impl.KDocLink, to avoid having to getPsi() + fun ASTNode.getLinkTextRange(): TextRange { + val text = text + if (text.startsWith('[') && text.endsWith(']')) { + return TextRange(1, text.length - 1) } + return TextRange(0, text.length) + } + + fun ASTNode.getLinkText(): String = getLinkTextRange().substring(text) + + val linkText = node.getLinkText().removeBackticksAndTrim() + ref.add(Reference(linkText.split('.').first(), false)) + ref.add(Reference(linkText.split('.').last(), false)) } REFERENCE_EXPRESSION, OPERATION_REFERENCE -> { @@ -110,7 +115,7 @@ public class NoUnusedImportsRule : ref.add( Reference( it.removeBackticksAndTrim(), - node.psi.parentDotQualifiedExpression() != null, + node.parentDotQualifiedExpression() != null, ), ) } @@ -281,9 +286,21 @@ public class NoUnusedImportsRule : private fun String.isComponentN() = COMPONENT_N_REGEX.matches(this) - private fun PsiElement.parentDotQualifiedExpression(): KtDotQualifiedExpression? { - val callOrThis = (parent as? KtCallExpression)?.takeIf { it.calleeExpression == this } ?: this - return (callOrThis.parent as? KtDotQualifiedExpression)?.takeIf { it.selectorExpression == callOrThis } + private fun ASTNode.parentDotQualifiedExpression(): KtDotQualifiedExpression? { + val callOrThis = + treeParent + .takeIf { it.elementType == ElementType.CALL_EXPRESSION } + ?.takeIf { it.findChildByType(EXPRESSION_SET) == this } + ?: this + return ( + callOrThis.treeParent + ?.takeIf { + it.elementType == ElementType.DOT_QUALIFIED_EXPRESSION + }?.psi as? KtDotQualifiedExpression + )?.takeIf { + it.selectorExpression?.node == + callOrThis + } } private fun String.removeBackticksAndTrim() = replace("`", "").trim() @@ -322,6 +339,38 @@ public class NoUnusedImportsRule : // by (https://github.com/shyiko/ktlint/issues/54) "getValue", "setValue", ) + + val EXPRESSION_SET = + TokenSet.create( + ElementType.LAMBDA_EXPRESSION, + ElementType.FUNCTION_LITERAL, + ElementType.ANNOTATED_EXPRESSION, + ElementType.REFERENCE_EXPRESSION, + ElementType.ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION, + ElementType.OPERATION_REFERENCE, + ElementType.LABEL, + ElementType.LABEL_QUALIFIER, + ElementType.THIS_EXPRESSION, + ElementType.SUPER_EXPRESSION, + ElementType.BINARY_EXPRESSION, + ElementType.BINARY_WITH_TYPE, + ElementType.IS_EXPRESSION, + ElementType.PREFIX_EXPRESSION, + ElementType.POSTFIX_EXPRESSION, + ElementType.LABELED_EXPRESSION, + ElementType.CALL_EXPRESSION, + ElementType.ARRAY_ACCESS_EXPRESSION, + ElementType.INDICES, + ElementType.DOT_QUALIFIED_EXPRESSION, + ElementType.CALLABLE_REFERENCE_EXPRESSION, + ElementType.CLASS_LITERAL_EXPRESSION, + ElementType.SAFE_ACCESS_EXPRESSION, + ElementType.OBJECT_LITERAL, + ElementType.WHEN, + ElementType.COLLECTION_LITERAL_EXPRESSION, + ElementType.TYPE_CODE_FRAGMENT, + ElementType.EXPRESSION_CODE_FRAGMENT, + ) } } From 7e2f282eb07f8c9761c80858d8893b0fc59c2b95 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 23:28:25 -0500 Subject: [PATCH 07/27] remove psi from SpacingAroundColonRule --- .../standard/rules/SpacingAroundColonRule.kt | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt index 66400cdc8b..1c740e8828 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ import com.pinterest.ktlint.rule.engine.core.api.RuleId @@ -21,15 +22,7 @@ import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement -import org.jetbrains.kotlin.psi.KtAnnotation -import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtConstructor -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.psi.KtTypeConstraint -import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull @@ -50,7 +43,7 @@ public class SpacingAroundColonRule : StandardRule("colon-spacing") { node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - val psiParent = node.psi.parent + val parentType = node.treeParent.elementType val prevLeaf = node.prevLeaf() if (prevLeaf != null && prevLeaf.isWhiteSpaceWithNewline()) { emit(prevLeaf.startOffset, "Unexpected newline before \":\"", true) @@ -62,7 +55,7 @@ public class SpacingAroundColonRule : StandardRule("colon-spacing") { .toList() .reversed() when { - psiParent is KtProperty || psiParent is KtNamedFunction -> { + parentType == ElementType.PROPERTY || parentType == ElementType.FUN -> { val equalsSignElement = node .siblings(forward = true) @@ -186,27 +179,29 @@ public class SpacingAroundColonRule : StandardRule("colon-spacing") { private inline val ASTNode.spacingBefore: Boolean get() = - when { - psi.parent is KtClassOrObject -> { + when (treeParent.elementType) { + + ElementType.CLASS, + ElementType.OBJECT_DECLARATION, + -> { true } - psi.parent is KtConstructor<*> -> { + ElementType.SECONDARY_CONSTRUCTOR -> { // constructor : this/super true } - psi.parent is KtTypeConstraint -> { + ElementType.TYPE_CONSTRAINT -> { // where T : S true } - psi.parent.parent is KtTypeParameterList -> { - true - } - else -> { - false + when (treeParent.treeParent.elementType) { + ElementType.TYPE_PARAMETER_LIST -> true + else -> false + } } } @@ -215,8 +210,8 @@ public class SpacingAroundColonRule : StandardRule("colon-spacing") { private inline val ASTNode.spacingAfter: Boolean get() = - when (psi.parent) { - is KtAnnotation, is KtAnnotationEntry -> true + when (treeParent.elementType) { + ElementType.ANNOTATION, ElementType.ANNOTATION_ENTRY -> true else -> false } From bd1596b12bfc99a2cede9848be0ab06fc3861d58 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 23:35:45 -0500 Subject: [PATCH 08/27] remove psi from SpacingAroundDoubleColonRule --- .../ruleset/standard/rules/SpacingAroundDoubleColonRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt index 692ff06ed7..10420a083b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt @@ -39,7 +39,7 @@ public class SpacingAroundDoubleColonRule : StandardRule("double-colon-spacing") // String::length, ::isOdd if (node.treePrev == null) { // compose(length, ::isOdd), val predicate = ::isOdd removeSingleWhiteSpace = true - !prevLeaf.textContains('\n') && prevLeaf.psi.textLength > 1 + !prevLeaf.textContains('\n') && prevLeaf.textLength > 1 } else { // String::length, List::isEmpty !prevLeaf.textContains('\n') } From d0c798eadaf2446fa87ff8f0406d87c7f2e8a43a Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 23:43:16 -0500 Subject: [PATCH 09/27] remove psi from StatementWrappingRule --- .../ruleset/standard/rules/StatementWrappingRule.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt index cfa3387d90..0461c403a2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt @@ -23,6 +23,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.hasModifier import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace @@ -34,8 +35,6 @@ import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.utils.addToStdlib.applyIf @SinceKtlint("0.50", EXPERIMENTAL) @@ -163,7 +162,7 @@ public class StatementWrappingRule : private inline val ASTNode.isEnumClassOnSingleLine: Boolean get() = - if (psi.isEnumClass) { + if (isEnumClass) { val lastChildLeaf = lastChildLeafOrSelf() // Ignore the leading comment noNewLineInClosedRange(firstCodeLeafOrNull!!, lastChildLeaf) @@ -182,8 +181,8 @@ public class StatementWrappingRule : }?.firstOrNull() ?.firstChildLeafOrSelf() - private inline val PsiElement.isEnumClass: Boolean - get() = (this as? KtClass)?.isEnum() ?: false + private inline val ASTNode.isEnumClass: Boolean + get() = elementType == ElementType.CLASS && hasModifier(ElementType.ENUM_KEYWORD) private inline val ASTNode.indentAsChild: String get() = indent().plus(indentConfig.indent) From 00583306c6016b2554a3de964273652c11af077a Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Mon, 9 Dec 2024 23:54:41 -0500 Subject: [PATCH 10/27] reduce psi in TrailingCommaOnDeclarationSiteRule --- .../TrailingCommaOnDeclarationSiteRule.kt | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt index b1058b77da..1ba6e741b1 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt @@ -38,7 +38,6 @@ import org.ec4j.core.model.PropertyType.PropertyValueParser import org.jetbrains.kotlin.KtNodeTypes.WHEN_ENTRY_GUARD import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet @@ -306,7 +305,12 @@ public class TrailingCommaOnDeclarationSiteRule : val leafBeforeArrowOrNull = leafBeforeArrowOrNull() val addNewLine = leafBeforeArrowOrNull - ?.let { !(leafBeforeArrowOrNull is PsiWhiteSpace && leafBeforeArrowOrNull.textContains('\n')) } + ?.let { + !( + leafBeforeArrowOrNull.elementType == ElementType.WHITE_SPACE && + leafBeforeArrowOrNull.textContains('\n') + ) + } ?: false val prevNode = inspectNode.prevCodeLeaf()!! if (addNewLine) { @@ -327,8 +331,8 @@ public class TrailingCommaOnDeclarationSiteRule : prevNode .treeParent .indent() - if (leafBeforeArrowOrNull is PsiWhiteSpace) { - (leafBeforeArrowOrNull as LeafPsiElement).rawReplaceWithText(indent) + if (leafBeforeArrowOrNull.elementType == ElementType.WHITE_SPACE) { + (leafBeforeArrowOrNull.psi as LeafPsiElement).rawReplaceWithText(indent) } else { inspectNode .prevCodeLeaf() @@ -341,7 +345,7 @@ public class TrailingCommaOnDeclarationSiteRule : if (inspectNode.treeParent.elementType == ElementType.ENUM_ENTRY) { val parentIndent = - (prevNode.psi.parent.prevLeaf() as? PsiWhiteSpace)?.text + (prevNode.treeParent.prevLeaf()?.takeIf { it.elementType == ElementType.WHITE_SPACE })?.text ?: prevNode.indent() (inspectNode as LeafPsiElement).apply { this.treeParent.addChild(LeafPsiElement(COMMA, ","), this) @@ -416,17 +420,11 @@ public class TrailingCommaOnDeclarationSiteRule : } private fun ASTNode.leafBeforeArrowOrNull() = - when (psi) { - is KtWhenEntry -> { - (psi as KtWhenEntry) - .arrow - ?.prevLeaf() - } - - is KtFunctionLiteral -> { - (psi as KtFunctionLiteral) - .arrow - ?.prevLeaf() + when (elementType) { + ElementType.WHEN_ENTRY, + ElementType.FUNCTION_LITERAL, + -> { + findChildByType(ElementType.ARROW)?.prevLeaf() } else -> { From a10fd38636071b4e6bcacb52a5ed9b738a39a307 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Tue, 10 Dec 2024 00:12:10 -0500 Subject: [PATCH 11/27] deprecate and replace usages of "isPartOf(klass: KClass)" --- .../api/ktlint-rule-engine-core.api | 2 ++ .../rule/engine/core/api/ASTNodeExtension.kt | 15 ++++++++++----- .../ruleset/standard/rules/MaxLineLengthRule.kt | 14 ++++++-------- .../standard/rules/NoEmptyClassBodyRule.kt | 4 ++-- .../standard/rules/SpacingAroundOperatorsRule.kt | 4 ++-- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index 24d4d0431e..aafbd92d76 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -5,6 +5,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun children (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lkotlin/sequences/Sequence; public static final fun findCompositeParentElementOfType (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun firstChildLeafOrSelf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; + public static final fun getCOMMENT_TOKENS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; public static final fun getColumn (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I public static final fun hasModifier (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun hasNewLineInClosedRange (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z @@ -14,6 +15,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun isLeaf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z public static final fun isPartOf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/reflect/KClass;)Z public static final fun isPartOf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z + public static final fun isPartOf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet;)Z public static final fun isPartOfComment (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z public static final fun isPartOfCompositeElementOfType (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun isPartOfString (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index e304fbf6bd..d63ba26cfc 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType +import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.psiUtil.leaves import org.jetbrains.kotlin.util.prefixIfNot import org.jetbrains.kotlin.utils.addToStdlib.applyIf @@ -182,11 +183,17 @@ public fun ASTNode.parent( return null } +public fun ASTNode.isPartOf(tokenSet: TokenSet): Boolean = parent(predicate = { tokenSet.contains(it.elementType) }, strict = false) != null + /** * @param elementType [ElementType].* */ public fun ASTNode.isPartOf(elementType: IElementType): Boolean = parent(elementType, strict = false) != null +@Deprecated( + "psi is a performance issue, see https://github.com/pinterest/ktlint/pull/2901", + replaceWith = ReplaceWith("ASTNode.isPartOf(elementType: IElementType) or ASTNode.isPartOf(tokenSet: TokenSet)"), +) public fun ASTNode.isPartOf(klass: KClass): Boolean { var n: ASTNode? = this while (n != null) { @@ -222,11 +229,9 @@ public fun ASTNode.isLeaf(): Boolean = firstChildNode == null */ public fun ASTNode.isCodeLeaf(): Boolean = isLeaf() && !isWhiteSpace() && !isPartOfComment() -public fun ASTNode.isPartOfComment(): Boolean = - parent(strict = false) { - val eType = it.elementType - eType == ElementType.BLOCK_COMMENT || eType == ElementType.EOL_COMMENT || eType == ElementType.KDOC - } != null +public fun ASTNode.isPartOfComment(): Boolean = isPartOf(COMMENT_TOKENS) + +public val COMMENT_TOKENS: TokenSet = TokenSet.create(ElementType.BLOCK_COMMENT, ElementType.EOL_COMMENT, ElementType.KDOC) public fun ASTNode.children(): Sequence = generateSequence(firstChildNode) { node -> node.treeNext } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt index 87b2e8cebf..3d751b85d2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MaxLineLengthRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.COMMA import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER import com.pinterest.ktlint.rule.engine.core.api.ElementType.STRING_TEMPLATE @@ -16,6 +17,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RULE_EXECUTION_PRO import com.pinterest.ktlint.rule.engine.core.api.editorconfig.RuleExecution import com.pinterest.ktlint.rule.engine.core.api.editorconfig.ktLintRuleExecutionPropertyName import com.pinterest.ktlint.rule.engine.core.api.isPartOf +import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.leavesOnLine @@ -26,11 +28,7 @@ import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement -import org.jetbrains.kotlin.kdoc.psi.api.KDoc -import org.jetbrains.kotlin.psi.KtImportDirective -import org.jetbrains.kotlin.psi.KtPackageDirective @SinceKtlint("0.9", STABLE) public class MaxLineLengthRule : @@ -82,9 +80,9 @@ public class MaxLineLengthRule : .takeIf { it is LeafPsiElement } ?.takeIf { it.nextLeaf() == null || it.nextLeaf().isWhiteSpaceWithNewline() } ?.takeIf { it.lineLength() > maxLineLength } - ?.takeUnless { it.isPartOf(KtPackageDirective::class) } - ?.takeUnless { it.isPartOf(KtImportDirective::class) } - ?.takeUnless { it.isPartOf(KDoc::class) } + ?.takeUnless { it.isPartOf(ElementType.PACKAGE_DIRECTIVE) } + ?.takeUnless { it.isPartOf(ElementType.IMPORT_DIRECTIVE) } + ?.takeUnless { it.isPartOf(ElementType.KDOC) } ?.takeUnless { it.isPartOfRawMultiLineString() } ?.takeUnless { it.isLineOnlyContainingSingleTemplateString() } ?.takeUnless { it.elementType == COMMA && it.prevLeaf()?.isLineOnlyContainingSingleTemplateString() ?: false } @@ -130,7 +128,7 @@ public class MaxLineLengthRule : ?: false private fun ASTNode.isLineOnlyContainingComment() = - isPartOf(PsiComment::class) && + isPartOfComment() && (prevLeaf() == null || prevLeaf().isWhiteSpaceWithNewline()) public companion object { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt index ed68b38340..55cf24a32a 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoEmptyClassBodyRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE @@ -15,7 +16,6 @@ import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.remove import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.psi.KtObjectLiteralExpression @SinceKtlint("0.9", STABLE) public class NoEmptyClassBodyRule : StandardRule("no-empty-class-body") { @@ -28,7 +28,7 @@ public class NoEmptyClassBodyRule : StandardRule("no-empty-class-body") { n.elementType == LBRACE && n.nextLeaf { it.elementType != WHITE_SPACE }?.elementType == RBRACE } == true && - !node.isPartOf(KtObjectLiteralExpression::class) && + !node.isPartOf(ElementType.OBJECT_LITERAL) && node .treeParent .firstChildNode diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt index c05cb938a6..43e56a4361 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundOperatorsRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANDAND import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW import com.pinterest.ktlint.rule.engine.core.api.ElementType.DIV @@ -41,7 +42,6 @@ import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet -import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtOperationExpression import org.jetbrains.kotlin.psi.KtPrefixExpression @@ -118,7 +118,7 @@ public class SpacingAroundOperatorsRule : StandardRule("op-spacing") { private fun ASTNode.isImport() = // import * - isPartOf(KtImportDirective::class) + isPartOf(ElementType.IMPORT_DIRECTIVE) private companion object { private val OPERATORS = From 94624bafd0a06f433ad4bf083f504cb5d48dca91 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Tue, 10 Dec 2024 02:12:53 -0500 Subject: [PATCH 12/27] remove psi from SuppressionLocator --- .../engine/internal/SuppressionLocator.kt | 115 ++++++++++-------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt index d0f0433555..fc59a1dc7b 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt @@ -1,20 +1,19 @@ package com.pinterest.ktlint.rule.engine.internal +import com.pinterest.ktlint.rule.engine.core.api.COMMENT_TOKENS +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE import com.pinterest.ktlint.rule.engine.core.api.IgnoreKtlintSuppressions import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.nextSibling +import com.pinterest.ktlint.rule.engine.core.api.parent +import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.internal.SuppressionLocator.CommentSuppressionHint.Type.BLOCK_END import com.pinterest.ktlint.rule.engine.internal.SuppressionLocator.CommentSuppressionHint.Type.BLOCK_START import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment -import org.jetbrains.kotlin.psi.KtAnnotated -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.ValueArgument -import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet internal class SuppressionLocator( editorConfig: EditorConfig, @@ -57,34 +56,27 @@ internal class SuppressionLocator( private fun findSuppressionHints(rootNode: ASTNode): List { val suppressionHints = ArrayList() val commentSuppressionsHints = mutableListOf() - rootNode.findSuppressionHints { node -> - when (val psi = node.psi) { - is PsiComment -> { + rootNode.recursiveChildren(includeSelf = true).forEach { node -> + val eType = node.elementType + when { + COMMENT_TOKENS.contains(eType) -> { node .createSuppressionHintFromComment() ?.let { commentSuppressionsHints.add(it) } } - is KtAnnotated -> { - psi - .createSuppressionHintFromAnnotations() - ?.let { suppressionHints.add(it) } + eType == ElementType.ANNOTATION_ENTRY -> { + createSuppressionHintFromAnnotations( + annotation = node, + )?.let { suppressionHints.add(it) } } } } - return suppressionHints.plus( commentSuppressionsHints.toSuppressionHints(), ) } - private fun ASTNode.findSuppressionHints(block: (node: ASTNode) -> Unit) { - block(this) - this - .getChildren(null) - .forEach { it.findSuppressionHints(block) } - } - private fun ASTNode.createSuppressionHintFromComment(): CommentSuppressionHint? = text .removePrefix("//") @@ -178,42 +170,41 @@ internal class SuppressionLocator( private fun List.tail() = this.subList(1, this.size) - private fun KtAnnotated.createSuppressionHintFromAnnotations(): SuppressionHint? = - annotationEntries - .filter { - it - .calleeExpression - ?.constructorReferenceExpression - ?.getReferencedName() in SUPPRESS_ANNOTATIONS - }.flatMap(KtAnnotationEntry::getValueArguments) - .flatMap { it.findRuleSuppressionIds() } - .let { suppressedRuleIds -> - when { - suppressedRuleIds.isEmpty() -> { - null - } - - suppressedRuleIds.contains(ALL_KTLINT_RULES_SUPPRESSION_ID) -> { - SuppressionHint( - IntRange(startOffset, endOffset - 1), - emptySet(), - ) - } + private fun createSuppressionHintFromAnnotations(annotation: ASTNode): SuppressionHint? { + if (annotation + .findChildByType(ElementType.CONSTRUCTOR_CALLEE) + ?.findChildByType(ElementType.TYPE_REFERENCE) + ?.text !in SUPPRESS_ANNOTATIONS + ) { + return null + } - else -> { - SuppressionHint( - IntRange(startOffset, endOffset - 1), - suppressedRuleIds.toSet(), - ) - } + val suppressedRuleIds = + annotation + .recursiveChildren() + .filter { it.elementType == ElementType.VALUE_ARGUMENT } + .flatMapTo(mutableListOf()) { + it.text.findRuleSuppressionIds() } - } - private fun ValueArgument.findRuleSuppressionIds(): List = - getArgumentExpression() - ?.text - ?.removeSurrounding("\"") - ?.let { argumentExpressionText -> + if (suppressedRuleIds.isEmpty()) return null + + val owner = + annotation.parent { + ANNOTATED_ELEMENT_TYPES.contains(it.elementType) + } ?: return null + + val tr = owner.textRange + + return SuppressionHint( + IntRange(tr.startOffset, tr.endOffset - 1), + if (suppressedRuleIds.contains(ALL_KTLINT_RULES_SUPPRESSION_ID)) emptySet() else suppressedRuleIds.toSet(), + ) + } + + private fun String.findRuleSuppressionIds(): List = + removeSurrounding("\"") + .let { argumentExpressionText -> when { argumentExpressionText == "ktlint" -> { // Disable all rules @@ -280,5 +271,21 @@ internal class SuppressionLocator( ) val SUPPRESS_ANNOTATIONS = setOf("Suppress", "SuppressWarnings") const val ALL_KTLINT_RULES_SUPPRESSION_ID = "ktlint:suppress-all-rules" + val ANNOTATED_ELEMENT_TYPES = + TokenSet.create( + ElementType.CLASS, + ElementType.OBJECT_DECLARATION, + ElementType.ENUM_ENTRY, + ElementType.FUN, + ElementType.PROPERTY, + ElementType.PROPERTY_ACCESSOR, + ElementType.TYPE_PARAMETER, + ElementType.TYPEALIAS, + ElementType.PRIMARY_CONSTRUCTOR, + ElementType.SECONDARY_CONSTRUCTOR, + ElementType.ANNOTATED_EXPRESSION, + ElementType.EXPRESSION_CODE_FRAGMENT, + ElementType.FILE, + ) } } From e962b649b06363a414fb12bb3ee206da39887002 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Tue, 10 Dec 2024 03:11:15 -0500 Subject: [PATCH 13/27] simple psi removals --- .../api/ktlint-rule-engine-core.api | 1 + .../rule/engine/core/api/ASTNodeExtension.kt | 7 +++++++ .../standard/rules/AnnotationSpacingRule.kt | 5 ++--- .../rules/BlankLineBeforeDeclarationRule.kt | 4 ++-- .../standard/rules/ClassSignatureRule.kt | 4 ++-- .../standard/rules/EnumWrappingRule.kt | 5 +++-- .../standard/rules/FunctionSignatureRule.kt | 4 ++-- .../ruleset/standard/rules/IndentationRule.kt | 4 ++-- .../standard/rules/ModifierOrderRule.kt | 7 +++---- .../standard/rules/NoSemicolonsRule.kt | 9 +++------ .../standard/rules/NoTrailingSpacesRule.kt | 4 ++-- ...gBetweenDeclarationsWithAnnotationsRule.kt | 19 +++++++++---------- .../TrailingCommaOnDeclarationSiteRule.kt | 14 ++++++-------- .../ruleset/standard/rules/WrappingRule.kt | 4 ++-- 14 files changed, 46 insertions(+), 45 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index aafbd92d76..5b1e3cef45 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -7,6 +7,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun firstChildLeafOrSelf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun getCOMMENT_TOKENS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; public static final fun getColumn (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I + public static final fun getPrevSiblingIgnoringWhitespaceAndComments (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun hasModifier (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun hasNewLineInClosedRange (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z public static final fun indent (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Z)Ljava/lang/String; diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index d63ba26cfc..4cc8ee81c4 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.psiUtil.leaves +import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.util.prefixIfNot import org.jetbrains.kotlin.utils.addToStdlib.applyIf import kotlin.reflect.KClass @@ -574,3 +575,9 @@ public fun ASTNode.replaceWith(node: ASTNode) { public fun ASTNode.remove() { treeParent.removeChild(this) } + +public fun ASTNode.getPrevSiblingIgnoringWhitespaceAndComments(): ASTNode? = + siblings(forward = false) + .filter { + !it.isWhiteSpace() && !COMMENT_TOKENS.contains(it.elementType) + }.firstOrNull() diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt index ae1709a2b2..b7abfc0d6e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt @@ -79,7 +79,7 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { }, { // Disallow multiple white spaces as well as comments - if (it.psi is PsiWhiteSpace) { + if (it.isWhiteSpace()) { val s = it.text // Ensure at least one occurrence of two line breaks s.indexOf("\n") != s.lastIndexOf("\n") @@ -90,8 +90,7 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { ) if (next != null) { if (node.elementType != ElementType.FILE_ANNOTATION_LIST && next.isPartOfComment()) { - val psi = node.psi - emit(psi.endOffset, ERROR_MESSAGE, true) + emit(node.textRange.endOffset, ERROR_MESSAGE, true) .ifAutocorrectAllowed { // Special-case autocorrection when the annotation is separated from the annotated construct // by a comment: we need to swap the order of the comment and the annotation diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt index 29a28e18cb..8b02750821 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt @@ -33,8 +33,8 @@ import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.rule.engine.core.util.safeAs import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtFunctionLiteral +import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets /** * Insert a blank line before declarations. No blank line is inserted before between a class or method signature and the first declaration @@ -150,7 +150,7 @@ public class BlankLineBeforeDeclarationRule : } node - .takeIf { it.psi is KtDeclaration } + .takeIf { KtTokenSets.DECLARATION_TYPES.contains(it.elementType) } ?.takeIf { val prevLeaf = it.prevLeaf() prevLeaf != null && (!prevLeaf.isWhiteSpace() || !prevLeaf.text.startsWith("\n\n")) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt index 381f96ad3d..0d294a2bb2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ClassSignatureRule.kt @@ -39,6 +39,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PR import com.pinterest.ktlint.rule.engine.core.api.hasModifier import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent +import com.pinterest.ktlint.rule.engine.core.api.isLeaf import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -54,7 +55,6 @@ import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement /** * Formats the class signature according to https://kotlinlang.org/docs/coding-conventions.html#class-headers @@ -651,7 +651,7 @@ public class ClassSignatureRule : private fun List.collectLeavesRecursively(): List = flatMap { it.collectLeavesRecursively() } private fun ASTNode.collectLeavesRecursively(): List = - if (psi is LeafElement) { + if (isLeaf()) { listOf(this) } else { children() diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt index 63d9ba4080..bb18315407 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumWrappingRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY @@ -17,6 +18,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf +import com.pinterest.ktlint.rule.engine.core.api.hasModifier import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -29,7 +31,6 @@ import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.psi.KtClass /** * @@ -61,7 +62,7 @@ public class EnumWrappingRule : ) { node .takeIf { node.elementType == CLASS } - ?.takeIf { (node.psi as KtClass).isEnum() } + ?.takeIf { node.hasModifier(ElementType.ENUM_KEYWORD) } ?.findChildByType(CLASS_BODY) ?.let { classBody -> visitEnumClass(classBody, emit) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt index 20cda849ac..cb3e21e25f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt @@ -38,6 +38,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PR import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isCodeLeaf +import com.pinterest.ktlint.rule.engine.core.api.isLeaf import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf @@ -56,7 +57,6 @@ import com.pinterest.ktlint.ruleset.standard.rules.FunctionSignatureRule.Functio import org.ec4j.core.model.PropertyType import org.ec4j.core.model.PropertyType.PropertyValueParser.EnumValueParser import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.utils.addToStdlib.ifTrue @@ -700,7 +700,7 @@ public class FunctionSignatureRule : private fun List.collectLeavesRecursively(): List = flatMap { it.collectLeavesRecursively() } private fun ASTNode.collectLeavesRecursively(): List = - if (psi is LeafElement) { + if (isLeaf()) { listOf(this) } else { children() diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index 10495e83a4..d547e09723 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -2,6 +2,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.COMMENT_TOKENS import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -124,7 +125,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.ec4j.core.model.PropertyType import org.ec4j.core.model.PropertyType.PropertyValueParser import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.psiUtil.leaves @@ -1191,7 +1191,7 @@ public class IndentationRule : } nextLeaf - ?.parent(strict = false) { it.psi is PsiComment } + ?.parent(strict = false) { COMMENT_TOKENS.contains(it.elementType) } ?.let { comment -> if (text.endsWith("\n")) { processedButNoIndentationChangedNeeded() diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt index 021243e44b..2f10b864f3 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ModifierOrderRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ABSTRACT_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.ACTUAL_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -34,8 +35,6 @@ import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtDeclarationModifierList @SinceKtlint("0.7", STABLE) public class ModifierOrderRule : StandardRule("modifier-order") { @@ -43,7 +42,7 @@ public class ModifierOrderRule : StandardRule("modifier-order") { node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - if (node.psi is KtDeclarationModifierList) { + if (node.elementType == ElementType.MODIFIER_LIST) { val modifierArr = node.getChildren(tokenSet) val sorted = modifierArr.copyOf().apply { sortWith(compareBy { ORDERED_MODIFIERS.indexOf(it.elementType) }) } if (!modifierArr.contentEquals(sorted)) { @@ -69,7 +68,7 @@ public class ModifierOrderRule : StandardRule("modifier-order") { } private fun squashAnnotations(sorted: Array): List { - val nonAnnotationModifiers = sorted.filter { it.psi !is KtAnnotationEntry } + val nonAnnotationModifiers = sorted.filter { it.elementType != ElementType.ANNOTATION_ENTRY } return if (nonAnnotationModifiers.size != sorted.size) { listOf("@Annotation...") + nonAnnotationModifiers.map { it.text } } else { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt index d4ccd8b115..d571ab863b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY import com.pinterest.ktlint.rule.engine.core.api.ElementType.ENUM_ENTRY @@ -26,13 +27,10 @@ import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace -import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtDoWhileExpression import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtLoopExpression -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType @SinceKtlint("0.1", STABLE) public class NoSemicolonsRule : @@ -84,11 +82,10 @@ public class NoSemicolonsRule : this is PsiWhiteSpace -> { nextLeaf { - val psi = it.psi it !is PsiWhiteSpace && it !is PsiComment && - psi.getStrictParentOfType() == null && - psi.getStrictParentOfType() == null + it.parent(ElementType.KDOC) == null && + it.parent(ElementType.ANNOTATION_ENTRY) == null }.let { nextLeaf -> nextLeaf == null || // \s+ and then eof diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt index 3d78892c69..f7df20a84b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE import com.pinterest.ktlint.rule.engine.core.api.RuleId @@ -13,7 +14,6 @@ import com.pinterest.ktlint.rule.engine.core.api.parent import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement -import org.jetbrains.kotlin.kdoc.psi.api.KDoc @SinceKtlint("0.1", STABLE) public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { @@ -74,7 +74,7 @@ public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { } } - private fun ASTNode.isPartOfKDoc() = parent(strict = false) { it.psi is KDoc } != null + private fun ASTNode.isPartOfKDoc() = parent(strict = false) { it.elementType == ElementType.KDOC } != null private fun ASTNode.hasTrailingSpacesBeforeNewline() = text.contains(SPACE_OR_TAB_BEFORE_NEWLINE_REGEX) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt index bb0e59c8c7..f9531fcd8d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt @@ -1,12 +1,14 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE import com.pinterest.ktlint.rule.engine.core.api.children +import com.pinterest.ktlint.rule.engine.core.api.getPrevSiblingIgnoringWhitespaceAndComments import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment @@ -16,11 +18,9 @@ import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments import org.jetbrains.kotlin.psi.psiUtil.leaves +import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets /** * @see https://youtrack.jetbrains.com/issue/KT-35106 @@ -32,7 +32,7 @@ public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spaci node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - if (node.psi is KtDeclaration && node.isAnnotated()) { + if (KtTokenSets.DECLARATION_TYPES.contains(node.elementType) && node.isAnnotated()) { visitDeclaration(node, emit) } } @@ -42,9 +42,8 @@ public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spaci emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node - .psi - ?.getPrevSiblingIgnoringWhitespaceAndComments(withItself = false) - ?.takeIf { it is KtDeclaration } + ?.getPrevSiblingIgnoringWhitespaceAndComments() + ?.takeIf { KtTokenSets.DECLARATION_TYPES.contains(it.elementType) } ?.takeIf { prevDeclaration -> hasNoBlankLineBetweenDeclarations(node, prevDeclaration) } ?.let { val prevLeaf = node.prevCodeLeaf()?.nextLeaf { it.isWhiteSpace() }!! @@ -61,16 +60,16 @@ public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spaci private fun ASTNode.isAnnotated(): Boolean = findChildByType(MODIFIER_LIST) ?.children() - ?.any { it.psi is KtAnnotationEntry } + ?.any { it.elementType == ElementType.ANNOTATION_ENTRY } ?: false private fun hasNoBlankLineBetweenDeclarations( node: ASTNode, - prevDeclaration: PsiElement, + prevDeclaration: ASTNode, ) = node .leaves(false) .takeWhile { it.isWhiteSpace() || it.isPartOfComment() } - .takeWhile { it.psi != prevDeclaration } + .takeWhile { it != prevDeclaration } .none { it.isBlankLine() } private fun ASTNode.isBlankLine() = isWhiteSpace() && text.count { it == '\n' } > 1 diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt index 1ba6e741b1..63f71446eb 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt @@ -22,6 +22,7 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE import com.pinterest.ktlint.rule.engine.core.api.children import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty +import com.pinterest.ktlint.rule.engine.core.api.hasModifier import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isCodeLeaf @@ -41,10 +42,8 @@ import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet -import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression import org.jetbrains.kotlin.psi.KtDestructuringDeclaration -import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtParameterList import org.jetbrains.kotlin.psi.KtValueArgumentList @@ -191,11 +190,10 @@ public class TrailingCommaOnDeclarationSiteRule : node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - val psi = node.psi - require(psi is KtClass) + require(node.elementType == ElementType.CLASS) node - .takeIf { psi.isEnum() } + .takeIf { node.hasModifier(ElementType.ENUM_KEYWORD) } ?.findChildByType(ElementType.CLASS_BODY) ?.takeUnless { it.noEnumEntries() } ?.let { classBody -> @@ -223,12 +221,12 @@ public class TrailingCommaOnDeclarationSiteRule : } } - private fun ASTNode.noEnumEntries() = children().none { it.psi is KtEnumEntry } + private fun ASTNode.noEnumEntries() = children().none { it.elementType == ElementType.ENUM_ENTRY } private fun ASTNode.lastTwoEnumEntriesAreOnSameLine(): Boolean { val lastTwoEnumEntries = children() - .filter { it.psi is KtEnumEntry } + .filter { it.elementType == ElementType.ENUM_ENTRY } .toList() .takeLast(2) @@ -243,7 +241,7 @@ public class TrailingCommaOnDeclarationSiteRule : */ private fun ASTNode.findNodeAfterLastEnumEntry() = children() - .lastOrNull { it.psi is KtEnumEntry } + .lastOrNull { it.elementType == ElementType.ENUM_ENTRY } ?.children() ?.singleOrNull { it.elementType == SEMICOLON } ?: lastChildNode diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt index c4551d4b35..c69e8ae877 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt @@ -3,6 +3,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.COMMENT_TOKENS import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW @@ -78,7 +79,6 @@ import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import io.github.oshai.kotlinlogging.KotlinLogging import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet @@ -386,7 +386,7 @@ public class WrappingRule : // value( // ), // a comment // c, d - nextSibling.treeNext?.treeNext?.psi !is PsiComment + nextSibling.treeNext?.treeNext?.let { !COMMENT_TOKENS.contains(it.elementType) } != false ) { requireNewlineAfterLeaf(nextSibling, emit) } From c8d1a54fe1667a025f67f5a19e26fd8afda7eba0 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Tue, 10 Dec 2024 04:21:18 -0500 Subject: [PATCH 14/27] refactor token sets --- .../api/ktlint-rule-engine-core.api | 7 +++- .../rule/engine/core/api/ASTNodeExtension.kt | 6 +-- .../ktlint/rule/engine/core/api/TokenSets.kt | 38 +++++++++++++++++++ .../engine/internal/SuppressionLocator.kt | 4 +- .../ruleset/standard/rules/IndentationRule.kt | 4 +- .../standard/rules/NoUnusedImportsRule.kt | 36 +----------------- .../ruleset/standard/rules/WrappingRule.kt | 4 +- 7 files changed, 54 insertions(+), 45 deletions(-) create mode 100644 ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index 5b1e3cef45..c03bc71cad 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -5,7 +5,6 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun children (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lkotlin/sequences/Sequence; public static final fun findCompositeParentElementOfType (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun firstChildLeafOrSelf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; - public static final fun getCOMMENT_TOKENS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; public static final fun getColumn (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I public static final fun getPrevSiblingIgnoringWhitespaceAndComments (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun hasModifier (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z @@ -529,6 +528,12 @@ public final class com/pinterest/ktlint/rule/engine/core/api/SinceKtlint$Status public static fun values ()[Lcom/pinterest/ktlint/rule/engine/core/api/SinceKtlint$Status; } +public final class com/pinterest/ktlint/rule/engine/core/api/TokenSets { + public static final field INSTANCE Lcom/pinterest/ktlint/rule/engine/core/api/TokenSets; + public final fun getCOMMENTS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; + public final fun getEXPRESSIONS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; +} + public final class com/pinterest/ktlint/rule/engine/core/api/editorconfig/CodeStyleEditorConfigPropertyKt { public static final fun getCODE_STYLE_PROPERTY ()Lcom/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfigProperty; public static final fun getCODE_STYLE_PROPERTY_TYPE ()Lorg/ec4j/core/model/PropertyType$LowerCasingPropertyType; diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 4cc8ee81c4..070be428ba 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -230,9 +230,7 @@ public fun ASTNode.isLeaf(): Boolean = firstChildNode == null */ public fun ASTNode.isCodeLeaf(): Boolean = isLeaf() && !isWhiteSpace() && !isPartOfComment() -public fun ASTNode.isPartOfComment(): Boolean = isPartOf(COMMENT_TOKENS) - -public val COMMENT_TOKENS: TokenSet = TokenSet.create(ElementType.BLOCK_COMMENT, ElementType.EOL_COMMENT, ElementType.KDOC) +public fun ASTNode.isPartOfComment(): Boolean = isPartOf(TokenSets.COMMENTS) public fun ASTNode.children(): Sequence = generateSequence(firstChildNode) { node -> node.treeNext } @@ -579,5 +577,5 @@ public fun ASTNode.remove() { public fun ASTNode.getPrevSiblingIgnoringWhitespaceAndComments(): ASTNode? = siblings(forward = false) .filter { - !it.isWhiteSpace() && !COMMENT_TOKENS.contains(it.elementType) + !it.isWhiteSpace() && !TokenSets.COMMENTS.contains(it.elementType) }.firstOrNull() diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt new file mode 100644 index 0000000000..e0aceb6778 --- /dev/null +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt @@ -0,0 +1,38 @@ +package com.pinterest.ktlint.rule.engine.core.api + +import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet + +public object TokenSets { + public val COMMENTS: TokenSet = TokenSet.create(ElementType.BLOCK_COMMENT, ElementType.EOL_COMMENT, ElementType.KDOC) + public val EXPRESSIONS: TokenSet = + TokenSet.create( + ElementType.LAMBDA_EXPRESSION, + ElementType.FUNCTION_LITERAL, + ElementType.ANNOTATED_EXPRESSION, + ElementType.REFERENCE_EXPRESSION, + ElementType.ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION, + ElementType.OPERATION_REFERENCE, + ElementType.LABEL, + ElementType.LABEL_QUALIFIER, + ElementType.THIS_EXPRESSION, + ElementType.SUPER_EXPRESSION, + ElementType.BINARY_EXPRESSION, + ElementType.BINARY_WITH_TYPE, + ElementType.IS_EXPRESSION, + ElementType.PREFIX_EXPRESSION, + ElementType.POSTFIX_EXPRESSION, + ElementType.LABELED_EXPRESSION, + ElementType.CALL_EXPRESSION, + ElementType.ARRAY_ACCESS_EXPRESSION, + ElementType.INDICES, + ElementType.DOT_QUALIFIED_EXPRESSION, + ElementType.CALLABLE_REFERENCE_EXPRESSION, + ElementType.CLASS_LITERAL_EXPRESSION, + ElementType.SAFE_ACCESS_EXPRESSION, + ElementType.OBJECT_LITERAL, + ElementType.WHEN, + ElementType.COLLECTION_LITERAL_EXPRESSION, + ElementType.TYPE_CODE_FRAGMENT, + ElementType.EXPRESSION_CODE_FRAGMENT, + ) +} diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt index fc59a1dc7b..02e53876b2 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt @@ -1,11 +1,11 @@ package com.pinterest.ktlint.rule.engine.internal -import com.pinterest.ktlint.rule.engine.core.api.COMMENT_TOKENS import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE import com.pinterest.ktlint.rule.engine.core.api.IgnoreKtlintSuppressions import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.RuleId +import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.parent @@ -59,7 +59,7 @@ internal class SuppressionLocator( rootNode.recursiveChildren(includeSelf = true).forEach { node -> val eType = node.elementType when { - COMMENT_TOKENS.contains(eType) -> { + TokenSets.COMMENTS.contains(eType) -> { node .createSuppressionHintFromComment() ?.let { commentSuppressionsHints.add(it) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index d547e09723..f90707024b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -2,7 +2,6 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision -import com.pinterest.ktlint.rule.engine.core.api.COMMENT_TOKENS import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY @@ -91,6 +90,7 @@ import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.children import com.pinterest.ktlint.rule.engine.core.api.column import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY @@ -1191,7 +1191,7 @@ public class IndentationRule : } nextLeaf - ?.parent(strict = false) { COMMENT_TOKENS.contains(it.elementType) } + ?.parent(strict = false) { TokenSets.COMMENTS.contains(it.elementType) } ?.let { comment -> if (text.endsWith("\n")) { processedButNoIndentationChangedNeeded() diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt index 8c957255d2..f75fb7980d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt @@ -14,6 +14,7 @@ import com.pinterest.ktlint.rule.engine.core.api.IgnoreKtlintSuppressions import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.isRoot @@ -29,7 +30,6 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement -import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.MARKDOWN_LINK import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtImportDirective @@ -290,7 +290,7 @@ public class NoUnusedImportsRule : val callOrThis = treeParent .takeIf { it.elementType == ElementType.CALL_EXPRESSION } - ?.takeIf { it.findChildByType(EXPRESSION_SET) == this } + ?.takeIf { it.findChildByType(TokenSets.EXPRESSIONS) == this } ?: this return ( callOrThis.treeParent @@ -339,38 +339,6 @@ public class NoUnusedImportsRule : // by (https://github.com/shyiko/ktlint/issues/54) "getValue", "setValue", ) - - val EXPRESSION_SET = - TokenSet.create( - ElementType.LAMBDA_EXPRESSION, - ElementType.FUNCTION_LITERAL, - ElementType.ANNOTATED_EXPRESSION, - ElementType.REFERENCE_EXPRESSION, - ElementType.ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION, - ElementType.OPERATION_REFERENCE, - ElementType.LABEL, - ElementType.LABEL_QUALIFIER, - ElementType.THIS_EXPRESSION, - ElementType.SUPER_EXPRESSION, - ElementType.BINARY_EXPRESSION, - ElementType.BINARY_WITH_TYPE, - ElementType.IS_EXPRESSION, - ElementType.PREFIX_EXPRESSION, - ElementType.POSTFIX_EXPRESSION, - ElementType.LABELED_EXPRESSION, - ElementType.CALL_EXPRESSION, - ElementType.ARRAY_ACCESS_EXPRESSION, - ElementType.INDICES, - ElementType.DOT_QUALIFIED_EXPRESSION, - ElementType.CALLABLE_REFERENCE_EXPRESSION, - ElementType.CLASS_LITERAL_EXPRESSION, - ElementType.SAFE_ACCESS_EXPRESSION, - ElementType.OBJECT_LITERAL, - ElementType.WHEN, - ElementType.COLLECTION_LITERAL_EXPRESSION, - ElementType.TYPE_CODE_FRAGMENT, - ElementType.EXPRESSION_CODE_FRAGMENT, - ) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt index c69e8ae877..9ad5e7e6e9 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt @@ -3,7 +3,6 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.logger.api.initKtLintKLogger import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT -import com.pinterest.ktlint.rule.engine.core.api.COMMENT_TOKENS import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW @@ -50,6 +49,7 @@ import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRu import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.children import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY @@ -386,7 +386,7 @@ public class WrappingRule : // value( // ), // a comment // c, d - nextSibling.treeNext?.treeNext?.let { !COMMENT_TOKENS.contains(it.elementType) } != false + nextSibling.treeNext?.treeNext?.let { !TokenSets.COMMENTS.contains(it.elementType) } != false ) { requireNewlineAfterLeaf(nextSibling, emit) } From efd32be7bbd3f32eb999c5328b1fa34acf4f7c6f Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Tue, 10 Dec 2024 04:21:41 -0500 Subject: [PATCH 15/27] remove psi from BlankLineBeforeDeclarationRule --- .../standard/rules/BlankLineBeforeDeclarationRule.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt index 8b02750821..3d41d479a6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY @@ -30,10 +31,8 @@ import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling import com.pinterest.ktlint.rule.engine.core.api.prevCodeSibling import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe -import com.pinterest.ktlint.rule.engine.core.util.safeAs import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets /** @@ -181,10 +180,8 @@ public class BlankLineBeforeDeclarationRule : treeParent .takeIf { it.elementType == BLOCK && it.treeParent.elementType == FUNCTION_LITERAL } ?.treeParent - ?.psi - ?.safeAs() - ?.bodyExpression - ?.node + ?.takeIf { it.elementType == ElementType.FUNCTION_LITERAL } + ?.findChildByType(ElementType.BLOCK) ?.children() ?.firstOrNull { !it.isWhiteSpace() && !it.isPartOfComment() } From 6094819b9a2007a3a7ce29be62338e96fddf6df7 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Tue, 10 Dec 2024 04:30:39 -0500 Subject: [PATCH 16/27] remove psi from EnumEntryNameCaseRule --- .../ruleset/standard/rules/EnumEntryNameCaseRule.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt index 2fc96fdac7..cbd9ab7ed8 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/EnumEntryNameCaseRule.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL @@ -13,7 +14,7 @@ import com.pinterest.ktlint.ruleset.standard.rules.internal.regExIgnoringDiacrit import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement -import org.jetbrains.kotlin.psi.KtEnumEntry +import org.jetbrains.kotlin.psi.KtPsiUtil /** * https://kotlinlang.org/docs/coding-conventions.html#property-names @@ -57,8 +58,9 @@ public class EnumEntryNameCaseRule : if (node !is CompositeElement) { return } - val enumEntry = node.psi as? KtEnumEntry ?: return - val name = enumEntry.name ?: return + if (node.elementType != ElementType.ENUM_ENTRY) return + val nameNode = node.findChildByType(ElementType.IDENTIFIER) ?: return + val name = KtPsiUtil.unquoteIdentifier(nameNode.text) if (!name.matches(enumEntryCasingRegex)) { emit(node.startOffset, enumEntryCasingViolation, false) From e2846c1c6493870b6630eb47d83230574be3e591 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Thu, 19 Dec 2024 23:25:52 -0500 Subject: [PATCH 17/27] changs from review --- .../api/ktlint-rule-engine-core.api | 4 +- .../rule/engine/core/api/ASTNodeExtension.kt | 24 +++-- .../ktlint/rule/engine/core/api/TokenSets.kt | 98 +++++++++++++------ .../engine/core/api/ASTNodeExtensionTest.kt | 31 ++++++ .../rule/engine/internal/KtlintSuppression.kt | 5 +- .../engine/internal/SuppressionLocator.kt | 91 ++++++++--------- .../internal/rules/KtlintSuppressionRule.kt | 8 +- .../ruleset/standard/rules/AnnotationRule.kt | 8 +- .../standard/rules/AnnotationSpacingRule.kt | 6 +- .../rules/ArgumentListWrappingRule.kt | 9 +- .../rules/BlankLineBeforeDeclarationRule.kt | 7 +- .../ruleset/standard/rules/IndentationRule.kt | 2 +- .../standard/rules/NoUnusedImportsRule.kt | 50 ++++------ ...gBetweenDeclarationsWithAnnotationsRule.kt | 9 +- .../TrailingCommaOnDeclarationSiteRule.kt | 30 ++---- 15 files changed, 207 insertions(+), 175 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index c03bc71cad..12238125d1 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -3,10 +3,11 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun beforeCodeSibling (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun betweenCodeSiblings (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun children (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lkotlin/sequences/Sequence; + public static final fun endOffset (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I + public static final fun findChildByTypeRecursively (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;Z)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun findCompositeParentElementOfType (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun firstChildLeafOrSelf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun getColumn (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I - public static final fun getPrevSiblingIgnoringWhitespaceAndComments (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun hasModifier (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun hasNewLineInClosedRange (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z public static final fun indent (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Z)Ljava/lang/String; @@ -531,6 +532,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/SinceKtlint$Status public final class com/pinterest/ktlint/rule/engine/core/api/TokenSets { public static final field INSTANCE Lcom/pinterest/ktlint/rule/engine/core/api/TokenSets; public final fun getCOMMENTS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; + public final fun getCONTROL_FLOW_KEYWORDS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; public final fun getEXPRESSIONS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; } diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 070be428ba..17561699d1 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.psiUtil.leaves -import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.util.prefixIfNot import org.jetbrains.kotlin.utils.addToStdlib.applyIf import kotlin.reflect.KClass @@ -184,7 +183,7 @@ public fun ASTNode.parent( return null } -public fun ASTNode.isPartOf(tokenSet: TokenSet): Boolean = parent(predicate = { tokenSet.contains(it.elementType) }, strict = false) != null +public fun ASTNode.isPartOf(tokenSet: TokenSet): Boolean = parent(strict = false) { tokenSet.contains(it.elementType) } != null /** * @param elementType [ElementType].* @@ -192,8 +191,9 @@ public fun ASTNode.isPartOf(tokenSet: TokenSet): Boolean = parent(predicate = { public fun ASTNode.isPartOf(elementType: IElementType): Boolean = parent(elementType, strict = false) != null @Deprecated( - "psi is a performance issue, see https://github.com/pinterest/ktlint/pull/2901", - replaceWith = ReplaceWith("ASTNode.isPartOf(elementType: IElementType) or ASTNode.isPartOf(tokenSet: TokenSet)"), + "Marked for removal in Ktlint 2.x. Replace with ASTNode.isPartOf(elementType: IElementType) or ASTNode.isPartOf(tokenSet: TokenSet). " + + "This method might cause performance issues, see https://github.com/pinterest/ktlint/pull/2901", + replaceWith = ReplaceWith("this.isPartOf(elementTypeOrTokenSet)"), ) public fun ASTNode.isPartOf(klass: KClass): Boolean { var n: ASTNode? = this @@ -239,10 +239,7 @@ public fun ASTNode.recursiveChildren(includeSelf: Boolean = false): Sequence Unit): FileASTNode = transformCodeToAST(this) .apply(block) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt index 01e5309ff2..d87df0bd13 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt @@ -11,13 +11,13 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER_LIST +import com.pinterest.ktlint.rule.engine.core.api.findChildByTypeRecursively import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isRoot import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling -import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.core.api.replaceWith import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiElement @@ -202,8 +202,7 @@ private fun ASTNode.existingSuppressions() = ?: getValueArguments() private fun ASTNode.existingSuppressionsFromNamedArgumentOrNull(): Set? = - recursiveChildren() - .firstOrNull { it.elementType == ElementType.COLLECTION_LITERAL_EXPRESSION } + findChildByTypeRecursively(ElementType.COLLECTION_LITERAL_EXPRESSION, includeSelf = false) ?.run { children() .filter { it.elementType == ElementType.STRING_TEMPLATE } diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt index 02e53876b2..edac395fae 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt @@ -57,24 +57,22 @@ internal class SuppressionLocator( val suppressionHints = ArrayList() val commentSuppressionsHints = mutableListOf() rootNode.recursiveChildren(includeSelf = true).forEach { node -> - val eType = node.elementType - when { - TokenSets.COMMENTS.contains(eType) -> { + when (node.elementType) { + in TokenSets.COMMENTS -> { node .createSuppressionHintFromComment() - ?.let { commentSuppressionsHints.add(it) } + ?.let(commentSuppressionsHints::add) } - eType == ElementType.ANNOTATION_ENTRY -> { - createSuppressionHintFromAnnotations( - annotation = node, - )?.let { suppressionHints.add(it) } + ElementType.ANNOTATION_ENTRY -> { + node + .takeIf { it.isSuppressAnnotation() } + ?.createSuppressionHintFromAnnotations() + ?.let(suppressionHints::add) } } } - return suppressionHints.plus( - commentSuppressionsHints.toSuppressionHints(), - ) + return suppressionHints + commentSuppressionsHints.toSuppressionHints() } private fun ASTNode.createSuppressionHintFromComment(): CommentSuppressionHint? = @@ -170,62 +168,57 @@ internal class SuppressionLocator( private fun List.tail() = this.subList(1, this.size) - private fun createSuppressionHintFromAnnotations(annotation: ASTNode): SuppressionHint? { - if (annotation - .findChildByType(ElementType.CONSTRUCTOR_CALLEE) - ?.findChildByType(ElementType.TYPE_REFERENCE) - ?.text !in SUPPRESS_ANNOTATIONS - ) { - return null - } + private fun ASTNode.isSuppressAnnotation(): Boolean = + findChildByType(ElementType.CONSTRUCTOR_CALLEE) + ?.findChildByType(ElementType.TYPE_REFERENCE) + ?.text in SUPPRESS_ANNOTATIONS + private fun ASTNode.createSuppressionHintFromAnnotations(): SuppressionHint? { val suppressedRuleIds = - annotation - .recursiveChildren() + recursiveChildren() .filter { it.elementType == ElementType.VALUE_ARGUMENT } .flatMapTo(mutableListOf()) { - it.text.findRuleSuppressionIds() + it.text.removeSurrounding("\"").findRuleSuppressionIds() } if (suppressedRuleIds.isEmpty()) return null val owner = - annotation.parent { - ANNOTATED_ELEMENT_TYPES.contains(it.elementType) - } ?: return null + parent { it.elementType in ANNOTATED_ELEMENT_TYPES } + ?: return null - val tr = owner.textRange + val textRange = owner.textRange return SuppressionHint( - IntRange(tr.startOffset, tr.endOffset - 1), - if (suppressedRuleIds.contains(ALL_KTLINT_RULES_SUPPRESSION_ID)) emptySet() else suppressedRuleIds.toSet(), + IntRange(textRange.startOffset, textRange.endOffset - 1), + if (suppressedRuleIds.contains(ALL_KTLINT_RULES_SUPPRESSION_ID)) { + emptySet() + } else { + suppressedRuleIds.toSet() + }, ) } private fun String.findRuleSuppressionIds(): List = - removeSurrounding("\"") - .let { argumentExpressionText -> - when { - argumentExpressionText == "ktlint" -> { - // Disable all rules - listOf(ALL_KTLINT_RULES_SUPPRESSION_ID) - } + when { + this == "ktlint" -> { + // Disable all rules + listOf(ALL_KTLINT_RULES_SUPPRESSION_ID) + } - argumentExpressionText.startsWith("ktlint:") -> { - // Disable specific rule. For backwards compatibility prefix rules without rule set id with the "standard" rule set - // id. Note that the KtlintSuppressionRule will emit a lint violation on the id. So this fix is only applicable for - // code bases in which the rule and suppression id's have not yet been fixed. - argumentExpressionText - .removePrefix("ktlint:") - .let { listOf(RuleId.prefixWithStandardRuleSetIdWhenMissing(it)) } - } + startsWith("ktlint:") -> { + // Disable specific rule. For backwards compatibility prefix rules without rule set id with the "standard" rule set + // id. Note that the KtlintSuppressionRule will emit a lint violation on the id. So this fix is only applicable for + // code bases in which the rule and suppression id's have not yet been fixed. + removePrefix("ktlint:") + .let { listOf(RuleId.prefixWithStandardRuleSetIdWhenMissing(it)) } + } - else -> { - // Disable specific rule if the annotation value is mapped to a specific rule - SUPPRESS_ANNOTATION_RULE_MAP[argumentExpressionText] - } - } - }.orEmpty() + else -> { + // Disable specific rule if the annotation value is mapped to a specific rule + SUPPRESS_ANNOTATION_RULE_MAP[this].orEmpty() + } + } /** * @param range zero-based range of lines where lint errors should be suppressed diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt index 2ec4de1044..23cdd8a068 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt @@ -10,6 +10,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.STRING_TEMPLATE import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT import com.pinterest.ktlint.rule.engine.core.api.IgnoreKtlintSuppressions import com.pinterest.ktlint.rule.engine.core.api.RuleId +import com.pinterest.ktlint.rule.engine.core.api.findChildByTypeRecursively import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline @@ -17,7 +18,6 @@ import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.parent import com.pinterest.ktlint.rule.engine.core.api.prevLeaf -import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.core.api.remove import com.pinterest.ktlint.rule.engine.core.api.replaceWith import com.pinterest.ktlint.rule.engine.internal.KTLINT_SUPPRESSION_ID_ALL_RULES @@ -106,10 +106,8 @@ public class KtlintSuppressionRule( emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node - .recursiveChildren(includeSelf = true) - .firstOrNull { - it.elementType == ElementType.LITERAL_STRING_TEMPLATE_ENTRY - }?.let { literalStringTemplateEntry -> + .findChildByTypeRecursively(ElementType.LITERAL_STRING_TEMPLATE_ENTRY, includeSelf = true) + ?.let { literalStringTemplateEntry -> val prefixedSuppression = literalStringTemplateEntry .text diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt index 4376a518c6..9e78571a1f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt @@ -1,10 +1,10 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision -import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY +import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_TARGET import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_KEYWORD @@ -346,8 +346,8 @@ public class AnnotationRule : private fun ASTNode.isNotReceiverTargetAnnotation() = getAnnotationUseSiteTarget() != AnnotationUseSiteTarget.RECEIVER private fun ASTNode.getAnnotationUseSiteTarget(): AnnotationUseSiteTarget? = - takeIf { it.elementType == ElementType.ANNOTATION_ENTRY } - ?.findChildByType(ElementType.ANNOTATION_TARGET) + takeIf { it.elementType == ANNOTATION_ENTRY } + ?.findChildByType(ANNOTATION_TARGET) ?.let { USE_SITE_TARGETS[it.text] } private fun ASTNode.isAnnotationEntryWithValueArgumentList() = getAnnotationEntryValueArgumentList() != null @@ -467,7 +467,7 @@ public class AnnotationRule : FILE_ANNOTATION_LIST, MODIFIER_LIST, ) - val USE_SITE_TARGETS = AnnotationUseSiteTarget.entries.associate { it.renderName to it } + val USE_SITE_TARGETS = AnnotationUseSiteTarget.entries.associateBy { it.renderName } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt index b7abfc0d6e..a9ed2179b5 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationSpacingRule.kt @@ -6,6 +6,7 @@ import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.endOffset import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment @@ -90,7 +91,7 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { ) if (next != null) { if (node.elementType != ElementType.FILE_ANNOTATION_LIST && next.isPartOfComment()) { - emit(node.textRange.endOffset, ERROR_MESSAGE, true) + emit(node.endOffset(), ERROR_MESSAGE, true) .ifAutocorrectAllowed { // Special-case autocorrection when the annotation is separated from the annotated construct // by a comment: we need to swap the order of the comment and the annotation @@ -120,8 +121,7 @@ public class AnnotationSpacingRule : StandardRule("annotation-spacing") { if (whiteSpaces.isNotEmpty() && node.elementType != ElementType.FILE_ANNOTATION_LIST) { // Check to make sure there are multi breaks between annotations if (whiteSpaces.any { psi -> psi.textToCharArray().count { it == '\n' } > 1 }) { - val psi = node.psi - emit(psi.endOffset, ERROR_MESSAGE, true) + emit(node.endOffset(), ERROR_MESSAGE, true) .ifAutocorrectAllowed { removeIntraLineBreaks(node, annotations.last()) removeExtraLineBreaks(node) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt index 515fc145f8..3215cb1065 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ArgumentListWrappingRule.kt @@ -1,7 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision -import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLLECTION_LITERAL_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION @@ -20,6 +19,7 @@ import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRu import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY @@ -284,18 +284,13 @@ public class ArgumentListWrappingRule : private fun ASTNode.isOnSameLineAsControlFlowKeyword(): Boolean { var prevLeaf = prevLeaf() ?: return false - while (!prevLeaf.isControlFlowKeyword) { + while (prevLeaf.elementType !in TokenSets.CONTROL_FLOW_KEYWORDS) { if (prevLeaf.isWhiteSpaceWithNewline()) return false prevLeaf = prevLeaf.prevLeaf() ?: return false } return true } - private val ASTNode.isControlFlowKeyword: Boolean get() = - elementType == ElementType.IF_KEYWORD || - elementType == ElementType.WHILE_KEYWORD || - elementType == ElementType.DO_KEYWORD - public companion object { private const val UNSET_IGNORE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY = Int.MAX_VALUE public val IGNORE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY: EditorConfigProperty = diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt index 3d41d479a6..24c864649d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt @@ -1,7 +1,6 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision -import com.pinterest.ktlint.rule.engine.core.api.ElementType import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY @@ -149,7 +148,7 @@ public class BlankLineBeforeDeclarationRule : } node - .takeIf { KtTokenSets.DECLARATION_TYPES.contains(it.elementType) } + .takeIf { it.elementType in KtTokenSets.DECLARATION_TYPES } ?.takeIf { val prevLeaf = it.prevLeaf() prevLeaf != null && (!prevLeaf.isWhiteSpace() || !prevLeaf.text.startsWith("\n\n")) @@ -180,8 +179,8 @@ public class BlankLineBeforeDeclarationRule : treeParent .takeIf { it.elementType == BLOCK && it.treeParent.elementType == FUNCTION_LITERAL } ?.treeParent - ?.takeIf { it.elementType == ElementType.FUNCTION_LITERAL } - ?.findChildByType(ElementType.BLOCK) + ?.takeIf { it.elementType == FUNCTION_LITERAL } + ?.findChildByType(BLOCK) ?.children() ?.firstOrNull { !it.isWhiteSpace() && !it.isPartOfComment() } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index f90707024b..c868962f9a 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -1191,7 +1191,7 @@ public class IndentationRule : } nextLeaf - ?.parent(strict = false) { TokenSets.COMMENTS.contains(it.elementType) } + ?.parent(strict = false) { it.elementType in TokenSets.COMMENTS } ?.let { comment -> if (text.endsWith("\n")) { processedButNoIndentationChangedNeeded() diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt index f75fb7980d..944245aa7c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt @@ -27,7 +27,6 @@ import com.pinterest.ktlint.rule.engine.core.api.prevSibling import com.pinterest.ktlint.rule.engine.core.api.remove import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.MARKDOWN_LINK @@ -84,18 +83,11 @@ public class NoUnusedImportsRule : } MARKDOWN_LINK -> { - // Both of these are copied from from org.jetbrains.kotlin.kdoc.psi.impl.KDocLink, to avoid having to getPsi() - fun ASTNode.getLinkTextRange(): TextRange { - val text = text - if (text.startsWith('[') && text.endsWith(']')) { - return TextRange(1, text.length - 1) - } - return TextRange(0, text.length) - } - - fun ASTNode.getLinkText(): String = getLinkTextRange().substring(text) - - val linkText = node.getLinkText().removeBackticksAndTrim() + val linkText = + node + .text + .removeSurrounding("[", "]") + .removeBackticksAndTrim() ref.add(Reference(linkText.split('.').first(), false)) ref.add(Reference(linkText.split('.').last(), false)) } @@ -115,7 +107,7 @@ public class NoUnusedImportsRule : ref.add( Reference( it.removeBackticksAndTrim(), - node.parentDotQualifiedExpression() != null, + node.isParentDotQualifiedExpressionOrNull(), ), ) } @@ -286,23 +278,23 @@ public class NoUnusedImportsRule : private fun String.isComponentN() = COMPONENT_N_REGEX.matches(this) - private fun ASTNode.parentDotQualifiedExpression(): KtDotQualifiedExpression? { - val callOrThis = - treeParent - .takeIf { it.elementType == ElementType.CALL_EXPRESSION } - ?.takeIf { it.findChildByType(TokenSets.EXPRESSIONS) == this } - ?: this - return ( - callOrThis.treeParent - ?.takeIf { - it.elementType == ElementType.DOT_QUALIFIED_EXPRESSION - }?.psi as? KtDotQualifiedExpression - )?.takeIf { - it.selectorExpression?.node == - callOrThis - } + private fun ASTNode.isParentDotQualifiedExpressionOrNull(): Boolean { + val callExpressionOrThis = parentCallExpressionOrNull() ?: this + return callExpressionOrThis.isDotQualifiedExpression() } + private fun ASTNode.parentCallExpressionOrNull() = + treeParent + .takeIf { it.elementType == ElementType.CALL_EXPRESSION } + ?.takeIf { it.findChildByType(TokenSets.EXPRESSIONS) == this } + + private fun ASTNode.isDotQualifiedExpression() = + treeParent + ?.takeIf { it.elementType == DOT_QUALIFIED_EXPRESSION } + ?.let { it.psi as? KtDotQualifiedExpression } + ?.takeIf { it.selectorExpression?.node == this } + .let { it != null } + private fun String.removeBackticksAndTrim() = replace("`", "").trim() private data class Reference( diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt index f9531fcd8d..183c247f7a 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt @@ -8,17 +8,16 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE import com.pinterest.ktlint.rule.engine.core.api.children -import com.pinterest.ktlint.rule.engine.core.api.getPrevSiblingIgnoringWhitespaceAndComments import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf +import com.pinterest.ktlint.rule.engine.core.api.prevCodeSibling import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments import org.jetbrains.kotlin.psi.psiUtil.leaves import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets @@ -32,7 +31,7 @@ public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spaci node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - if (KtTokenSets.DECLARATION_TYPES.contains(node.elementType) && node.isAnnotated()) { + if (node.elementType in KtTokenSets.DECLARATION_TYPES && node.isAnnotated()) { visitDeclaration(node, emit) } } @@ -42,8 +41,8 @@ public class SpacingBetweenDeclarationsWithAnnotationsRule : StandardRule("spaci emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { node - ?.getPrevSiblingIgnoringWhitespaceAndComments() - ?.takeIf { KtTokenSets.DECLARATION_TYPES.contains(it.elementType) } + .prevCodeSibling() + ?.takeIf { it.elementType in KtTokenSets.DECLARATION_TYPES } ?.takeIf { prevDeclaration -> hasNoBlankLineBetweenDeclarations(node, prevDeclaration) } ?.let { val prevLeaf = node.prevCodeLeaf()?.nextLeaf { it.isWhiteSpace() }!! diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt index 63f71446eb..2e82158251 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt @@ -26,6 +26,8 @@ import com.pinterest.ktlint.rule.engine.core.api.hasModifier import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.isCodeLeaf +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.rule.engine.core.api.noNewLineInClosedRange import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf @@ -301,15 +303,7 @@ public class TrailingCommaOnDeclarationSiteRule : TrailingCommaState.MISSING -> { if (isTrailingCommaAllowed) { val leafBeforeArrowOrNull = leafBeforeArrowOrNull() - val addNewLine = - leafBeforeArrowOrNull - ?.let { - !( - leafBeforeArrowOrNull.elementType == ElementType.WHITE_SPACE && - leafBeforeArrowOrNull.textContains('\n') - ) - } - ?: false + val addNewLine = !(leafBeforeArrowOrNull?.isWhiteSpaceWithNewline() ?: true) val prevNode = inspectNode.prevCodeLeaf()!! if (addNewLine) { emit( @@ -329,7 +323,7 @@ public class TrailingCommaOnDeclarationSiteRule : prevNode .treeParent .indent() - if (leafBeforeArrowOrNull.elementType == ElementType.WHITE_SPACE) { + if (leafBeforeArrowOrNull.isWhiteSpace()) { (leafBeforeArrowOrNull.psi as LeafPsiElement).rawReplaceWithText(indent) } else { inspectNode @@ -343,7 +337,7 @@ public class TrailingCommaOnDeclarationSiteRule : if (inspectNode.treeParent.elementType == ElementType.ENUM_ENTRY) { val parentIndent = - (prevNode.treeParent.prevLeaf()?.takeIf { it.elementType == ElementType.WHITE_SPACE })?.text + (prevNode.treeParent.prevLeaf()?.takeIf { it.isWhiteSpace() })?.text ?: prevNode.indent() (inspectNode as LeafPsiElement).apply { this.treeParent.addChild(LeafPsiElement(COMMA, ","), this) @@ -418,17 +412,9 @@ public class TrailingCommaOnDeclarationSiteRule : } private fun ASTNode.leafBeforeArrowOrNull() = - when (elementType) { - ElementType.WHEN_ENTRY, - ElementType.FUNCTION_LITERAL, - -> { - findChildByType(ElementType.ARROW)?.prevLeaf() - } - - else -> { - null - } - } + takeIf { it.elementType == WHEN_ENTRY || it.elementType == FUNCTION_LITERAL } + ?.findChildByType(ARROW) + ?.prevLeaf() private fun ASTNode.findPreviousTrailingCommaNodeOrNull(): ASTNode? { val codeLeaf = From fe71a48af86c149f6a5c0defb9ca369782178b10 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Sun, 29 Dec 2024 13:16:03 -0500 Subject: [PATCH 18/27] add kdoc for findChildByTypeRecursively and endOffset --- .../ktlint/rule/engine/core/api/ASTNodeExtension.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 17561699d1..c6e06c144e 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -571,9 +571,16 @@ public fun ASTNode.remove() { treeParent.removeChild(this) } +/** + * Searches the receiver [ASTNode] recursively, returning the first child with type [elementType]. If none are found, returns [null]. + * If [includeSelf] is [true], includes the receiver in the search. The receiver would then be the first element searched, so it is guaranteed to be returned if it has type [elementType]. + */ public fun ASTNode.findChildByTypeRecursively( elementType: IElementType, includeSelf: Boolean, ): ASTNode? = recursiveChildren(includeSelf).firstOrNull { it.elementType == elementType } +/** + * Returns the end offset of the text of this [ASTNode] + */ public fun ASTNode.endOffset(): Int = textRange.endOffset From d92140de2cf6d079cac8ec30a0189690a3ac2fa2 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Sun, 29 Dec 2024 13:23:11 -0500 Subject: [PATCH 19/27] remove a getPsi() from TrailingCommaOnDeclarationSiteRule --- .../standard/rules/TrailingCommaOnDeclarationSiteRule.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt index 2e82158251..6366168d36 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt @@ -41,6 +41,7 @@ import org.ec4j.core.model.PropertyType.PropertyValueParser import org.jetbrains.kotlin.KtNodeTypes.WHEN_ENTRY_GUARD import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet @@ -324,7 +325,7 @@ public class TrailingCommaOnDeclarationSiteRule : .treeParent .indent() if (leafBeforeArrowOrNull.isWhiteSpace()) { - (leafBeforeArrowOrNull.psi as LeafPsiElement).rawReplaceWithText(indent) + (leafBeforeArrowOrNull as LeafElement).rawReplaceWithText(indent) } else { inspectNode .prevCodeLeaf() From ffda19b210b9d4f79057b509c7d6d0873579e428 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Sun, 29 Dec 2024 14:28:39 -0500 Subject: [PATCH 20/27] add contract to isWhiteSpace --- .../ktlint/rule/engine/core/api/ASTNodeExtension.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index c6e06c144e..a93b2bfa4f 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -17,6 +17,8 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.psiUtil.leaves import org.jetbrains.kotlin.util.prefixIfNot import org.jetbrains.kotlin.utils.addToStdlib.applyIf +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract import kotlin.reflect.KClass public fun ASTNode.nextLeaf( @@ -214,7 +216,13 @@ public fun ASTNode.findCompositeParentElementOfType(iElementType: IElementType): public fun ASTNode.isPartOfString(): Boolean = parent(STRING_TEMPLATE, strict = false) != null -public fun ASTNode?.isWhiteSpace(): Boolean = this != null && elementType == WHITE_SPACE +@OptIn(ExperimentalContracts::class) +public fun ASTNode?.isWhiteSpace(): Boolean { + contract { + returns(true) implies (this@isWhiteSpace != null) + } + return this != null && elementType == WHITE_SPACE +} public fun ASTNode?.isWhiteSpaceWithNewline(): Boolean = this != null && elementType == WHITE_SPACE && textContains('\n') From 3efbf7aa53cc155ddffab8ec90819db5af6d6904 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Sun, 29 Dec 2024 15:20:17 -0500 Subject: [PATCH 21/27] make token sets more maintainable --- .../api/ktlint-rule-engine-core.api | 1 - .../ktlint/rule/engine/core/api/TokenSets.kt | 83 ++++--------------- .../standard/rules/NoUnusedImportsRule.kt | 2 - 3 files changed, 18 insertions(+), 68 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index 12238125d1..aa6ce7e9f6 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -533,7 +533,6 @@ public final class com/pinterest/ktlint/rule/engine/core/api/TokenSets { public static final field INSTANCE Lcom/pinterest/ktlint/rule/engine/core/api/TokenSets; public final fun getCOMMENTS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; public final fun getCONTROL_FLOW_KEYWORDS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; - public final fun getEXPRESSIONS ()Lorg/jetbrains/kotlin/com/intellij/psi/tree/TokenSet; } public final class com/pinterest/ktlint/rule/engine/core/api/editorconfig/CodeStyleEditorConfigPropertyKt { diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt index 5eadcf78ae..d7ee3cec07 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/TokenSets.kt @@ -1,78 +1,31 @@ package com.pinterest.ktlint.rule.engine.core.api -import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARRAY_ACCESS_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_WITH_TYPE -import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT -import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALLABLE_REFERENCE_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_LITERAL_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLLECTION_LITERAL_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.DO_KEYWORD -import com.pinterest.ktlint.rule.engine.core.api.ElementType.ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT -import com.pinterest.ktlint.rule.engine.core.api.ElementType.EXPRESSION_CODE_FRAGMENT -import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL +import com.pinterest.ktlint.rule.engine.core.api.ElementType.FOR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.IF_KEYWORD -import com.pinterest.ktlint.rule.engine.core.api.ElementType.INDICES -import com.pinterest.ktlint.rule.engine.core.api.ElementType.IS_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC -import com.pinterest.ktlint.rule.engine.core.api.ElementType.LABEL -import com.pinterest.ktlint.rule.engine.core.api.ElementType.LABELED_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.LABEL_QUALIFIER -import com.pinterest.ktlint.rule.engine.core.api.ElementType.LAMBDA_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_LITERAL -import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE -import com.pinterest.ktlint.rule.engine.core.api.ElementType.POSTFIX_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.PREFIX_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.REFERENCE_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.SAFE_ACCESS_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.SUPER_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.THIS_EXPRESSION -import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_CODE_FRAGMENT -import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN +import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_KEYWORD +import com.pinterest.ktlint.rule.engine.core.api.ElementType.TRY_KEYWORD +import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHILE_KEYWORD import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.lexer.KtTokens public object TokenSets { - public val COMMENTS: TokenSet = TokenSet.create(BLOCK_COMMENT, EOL_COMMENT, KDOC) - public val EXPRESSIONS: TokenSet = - TokenSet.create( - ANNOTATED_EXPRESSION, - ARRAY_ACCESS_EXPRESSION, - BINARY_EXPRESSION, - BINARY_WITH_TYPE, - CALLABLE_REFERENCE_EXPRESSION, - CALL_EXPRESSION, - CLASS_LITERAL_EXPRESSION, - COLLECTION_LITERAL_EXPRESSION, - DOT_QUALIFIED_EXPRESSION, - ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION, - EXPRESSION_CODE_FRAGMENT, - FUNCTION_LITERAL, - INDICES, - IS_EXPRESSION, - LABEL, - LABELED_EXPRESSION, - LABEL_QUALIFIER, - LAMBDA_EXPRESSION, - OBJECT_LITERAL, - OPERATION_REFERENCE, - POSTFIX_EXPRESSION, - PREFIX_EXPRESSION, - REFERENCE_EXPRESSION, - SAFE_ACCESS_EXPRESSION, - SUPER_EXPRESSION, - THIS_EXPRESSION, - TYPE_CODE_FRAGMENT, - WHEN, - ) + public val COMMENTS: TokenSet = KtTokens.COMMENTS + + /** + * + * Reference: This is a subset of [KotlinExpressionParsing.EXPRESSION_FIRST] + */ public val CONTROL_FLOW_KEYWORDS: TokenSet = TokenSet.create( - DO_KEYWORD, - IF_KEYWORD, + IF_KEYWORD, // if + WHEN_KEYWORD, // when + TRY_KEYWORD, // try + OBJECT_KEYWORD, // object + // loop + FOR_KEYWORD, WHILE_KEYWORD, + DO_KEYWORD, ) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt index 944245aa7c..45e3b393cf 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt @@ -14,7 +14,6 @@ import com.pinterest.ktlint.rule.engine.core.api.IgnoreKtlintSuppressions import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE -import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed import com.pinterest.ktlint.rule.engine.core.api.isPartOf import com.pinterest.ktlint.rule.engine.core.api.isRoot @@ -286,7 +285,6 @@ public class NoUnusedImportsRule : private fun ASTNode.parentCallExpressionOrNull() = treeParent .takeIf { it.elementType == ElementType.CALL_EXPRESSION } - ?.takeIf { it.findChildByType(TokenSets.EXPRESSIONS) == this } private fun ASTNode.isDotQualifiedExpression() = treeParent From cb757a9d3f9f99854c6222d4c76d012a9c46fb27 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Sun, 29 Dec 2024 16:13:04 -0500 Subject: [PATCH 22/27] alternative solution to token set --- .../engine/internal/SuppressionLocator.kt | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt index edac395fae..ff4ac50c4a 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt @@ -13,7 +13,10 @@ import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.internal.SuppressionLocator.CommentSuppressionHint.Type.BLOCK_END import com.pinterest.ktlint.rule.engine.internal.SuppressionLocator.CommentSuppressionHint.Type.BLOCK_START import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType +import org.jetbrains.kotlin.psi.KtAnnotated +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementType internal class SuppressionLocator( editorConfig: EditorConfig, @@ -183,9 +186,7 @@ internal class SuppressionLocator( if (suppressedRuleIds.isEmpty()) return null - val owner = - parent { it.elementType in ANNOTATED_ELEMENT_TYPES } - ?: return null + val owner = parent { it.canBeAnnotated() } ?: return null val textRange = owner.textRange @@ -264,21 +265,27 @@ internal class SuppressionLocator( ) val SUPPRESS_ANNOTATIONS = setOf("Suppress", "SuppressWarnings") const val ALL_KTLINT_RULES_SUPPRESSION_ID = "ktlint:suppress-all-rules" - val ANNOTATED_ELEMENT_TYPES = - TokenSet.create( - ElementType.CLASS, - ElementType.OBJECT_DECLARATION, - ElementType.ENUM_ENTRY, - ElementType.FUN, - ElementType.PROPERTY, - ElementType.PROPERTY_ACCESSOR, - ElementType.TYPE_PARAMETER, - ElementType.TYPEALIAS, - ElementType.PRIMARY_CONSTRUCTOR, - ElementType.SECONDARY_CONSTRUCTOR, - ElementType.ANNOTATED_EXPRESSION, - ElementType.EXPRESSION_CODE_FRAGMENT, - ElementType.FILE, - ) + + private fun ASTNode.canBeAnnotated(): Boolean = + when (val elementType = elementType) { + ElementType.FILE -> { + true + } + + ElementType.ANNOTATED_EXPRESSION -> { + true + } + + else -> { + DUMMY_PSI_ELEMENTS.getOrPut(elementType) { + when (elementType) { + is KtStubElementType<*, *> -> elementType.createPsiFromAst(this) + else -> throw NotImplementedError("getting dummy psi for $elementType (${elementType::class}) not implemented") + } + } is KtAnnotated + } + } + + private val DUMMY_PSI_ELEMENTS = hashMapOf() } } From 4574a59e81cbdf6d907bf6dd64314f003d966594 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Mon, 30 Dec 2024 20:49:40 +0100 Subject: [PATCH 23/27] Refactor - use less Psi functions Add a new test case to cover for a regression problem --- ...cingBetweenDeclarationsWithCommentsRule.kt | 59 ++++++++++--------- ...BetweenDeclarationsWithCommentsRuleTest.kt | 12 ++++ 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt index 8663d50e5e..4b713cfb0d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRule.kt @@ -1,23 +1,21 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision -import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE -import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE +import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline +import com.pinterest.ktlint.rule.engine.core.api.prevCodeSibling import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.prevSibling import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment -import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments -import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement +import org.jetbrains.kotlin.psi.stubs.elements.KtTokenSets /** * @see https://youtrack.jetbrains.com/issue/KT-35088 @@ -29,29 +27,36 @@ public class SpacingBetweenDeclarationsWithCommentsRule : StandardRule("spacing- node: ASTNode, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { - if (node is PsiComment) { - val declaration = node.parent as? KtDeclaration ?: return - val isTailComment = node.startOffset > declaration.startOffset - if (isTailComment || declaration.getPrevSiblingIgnoringWhitespaceAndComments() !is KtDeclaration) return + node + .takeIf { it.elementType in TokenSets.COMMENTS } + .takeIf { node.prevLeaf().isWhiteSpaceWithNewline() } + .takeUnless { node.startOffset > (node.treeParent?.startOffset ?: node.startOffset) } + ?.takeIf { it.treeParent.isDeclaration() } + ?.takeIf { it.treeParent.prevCodeSibling().isDeclaration() } + ?.let { visitCommentBeforeDeclaration(node, emit) } + } - val prevSibling = declaration.node.prevSibling { it.elementType != WHITE_SPACE } - if (prevSibling != null && - prevSibling.elementType != FILE && - prevSibling !is PsiComment - ) { - if (declaration.prevSibling is PsiWhiteSpace && declaration.prevSibling.text.count { it == '\n' } < 2) { - emit( - node.startOffset, - "Declarations and declarations with comments should have an empty space between.", - true, - ).ifAutocorrectAllowed { - val indent = node.prevLeaf()?.text?.trim('\n') ?: "" - (declaration.prevSibling.node as LeafPsiElement).rawReplaceWithText("\n\n$indent") - } + private fun visitCommentBeforeDeclaration( + node: ASTNode, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + node + .treeParent + .prevSibling() + .takeIf { it.isWhiteSpace() && it.text.count { it == '\n' } < 2 } + ?.let { whiteSpace -> + emit( + node.startOffset, + "Declarations and declarations with comments should have an empty space between.", + true, + ).ifAutocorrectAllowed { + val indent = node.prevLeaf()?.text?.trim('\n') ?: "" + (whiteSpace as LeafElement).rawReplaceWithText("\n\n$indent") } } - } } + + private fun ASTNode?.isDeclaration() = this != null && elementType in KtTokenSets.DECLARATION_TYPES } public val SPACING_BETWEEN_DECLARATIONS_WITH_COMMENTS_RULE_ID: RuleId = SpacingBetweenDeclarationsWithCommentsRule().ruleId diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRuleTest.kt index e71b974e3b..3704b83ffa 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithCommentsRuleTest.kt @@ -135,4 +135,16 @@ class SpacingBetweenDeclarationsWithCommentsRuleTest { """.trimIndent() spacingBetweenDeclarationsWithCommentsRuleAssertThat(code).hasNoLintViolations() } + + @Test + fun `Given consecutive declarations, and the second declaration contains an inner comment on a separate line then do nothing`() { + val code = + """ + val bar = "bar" + val foo = + // Some comment + "foo" + """.trimIndent() + spacingBetweenDeclarationsWithCommentsRuleAssertThat(code).hasNoLintViolations() + } } From bcc14a6933277da2e1cd62e39527209445aeb68c Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Wed, 1 Jan 2025 21:08:29 +0100 Subject: [PATCH 24/27] Generify alternative solution to token set --- .../api/ktlint-rule-engine-core.api | 2 + .../rule/engine/core/api/ASTNodeExtension.kt | 59 +++++++++++++++++++ .../engine/core/api/ASTNodeExtensionTest.kt | 24 +++++++- .../engine/internal/SuppressionLocator.kt | 29 +-------- 4 files changed, 86 insertions(+), 28 deletions(-) diff --git a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api index aa6ce7e9f6..999f5df343 100644 --- a/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api +++ b/ktlint-rule-engine-core/api/ktlint-rule-engine-core.api @@ -3,6 +3,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun beforeCodeSibling (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun betweenCodeSiblings (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z public static final fun children (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lkotlin/sequences/Sequence; + public static final fun dummyPsiElement (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement; public static final fun endOffset (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I public static final fun findChildByTypeRecursively (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;Z)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; public static final fun findCompositeParentElementOfType (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; @@ -13,6 +14,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt public static final fun indent (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Z)Ljava/lang/String; public static synthetic fun indent$default (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZILjava/lang/Object;)Ljava/lang/String; public static final fun isCodeLeaf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z + public static final fun isKtAnnotated (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z public static final fun isLeaf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Z public static final fun isPartOf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/reflect/KClass;)Z public static final fun isPartOf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Z diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index a93b2bfa4f..eea3b28512 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -7,6 +7,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VAL_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.VARARG_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.VAR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE +import org.jetbrains.kotlin.KtNodeType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement @@ -14,7 +15,13 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.lexer.KtKeywordToken +import org.jetbrains.kotlin.lexer.KtToken +import org.jetbrains.kotlin.psi.KtAnnotated +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.psiUtil.leaves +import org.jetbrains.kotlin.psi.stubs.elements.KtFileElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementType import org.jetbrains.kotlin.util.prefixIfNot import org.jetbrains.kotlin.utils.addToStdlib.applyIf import kotlin.contracts.ExperimentalContracts @@ -592,3 +599,55 @@ public fun ASTNode.findChildByTypeRecursively( * Returns the end offset of the text of this [ASTNode] */ public fun ASTNode.endOffset(): Int = textRange.endOffset + +private val elementTypeCache = hashMapOf() + +/** + * Checks if the [AstNode] extends the [KtAnnotated] interface. Using this function to check the interface is more performant than checking + * whether `psi is KtAnnotated` as the psi does not need to be derived from [ASTNode]. + */ +public fun ASTNode.isKtAnnotated(): Boolean = psiType { it is KtAnnotated } + +private inline fun ASTNode.psiType(predicate: (psiElement: PsiElement) -> Boolean): Boolean = + elementTypeCache + .getOrPut(elementType) { + // Create a dummy Psi element based on the current node, so that we can store the Psi Type for this ElementType. + // Creating this cache entry once per elementType is cheaper than accessing the psi for every node. + when (elementType) { + is KtFileElementType -> this.psi + is KtKeywordToken -> this.psi + is KtNodeType -> (elementType as KtNodeType).createPsi(this) + is KtStubElementType<*, *> -> (elementType as KtStubElementType<*, *>).createPsiFromAst(this) + is KtToken -> this.psi + else -> throw NotImplementedError("Cannot create dummy psi for $elementType (${elementType::class})") + } + }.let { predicate(it) } + +/** + * Checks if the [AstNode] extends the [T] interface which implements [KtElement]. Call this function like: + * ``` + * astNode.isPsiType() + * ``` + * Using this function to check the [PsiElement] type of the [ASTNode] is more performant than checking whether `astNode.psi is KtAnnotated` + * as the psi does not need to be derived from [ASTNode]. + */ +public inline fun ASTNode.isPsiType(): Boolean = this.dummyPsiElement() is T + +/** + * FOR INTERNAL USE ONLY. The returned element is a stub version of a [PsiElement] of the same type as the given [ASTNode]. The returned + * result may only be used to validate the type of the [PsiElement]. + */ +public fun ASTNode.dummyPsiElement(): PsiElement = + elementTypeCache + .getOrPut(elementType) { + // Create a dummy Psi element based on the current node, so that we can store the Psi Type for this ElementType. + // Creating this cache entry once per elementType is cheaper than accessing the psi for every node. + when (elementType) { + is KtFileElementType -> this.psi + is KtKeywordToken -> this.psi + is KtNodeType -> (elementType as KtNodeType).createPsi(this) + is KtStubElementType<*, *> -> (elementType as KtStubElementType<*, *>).createPsiFromAst(this) + is KtToken -> this.psi + else -> throw NotImplementedError("Cannot create dummy psi for $elementType (${elementType::class})") + } + } diff --git a/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt b/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt index 7410a52b12..10e87a6096 100644 --- a/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt +++ b/ktlint-rule-engine-core/src/test/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionTest.kt @@ -5,6 +5,7 @@ import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_BODY +import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.ENUM_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN @@ -25,6 +26,7 @@ import org.assertj.core.api.Assertions.assertThatNoException import org.assertj.core.api.Assertions.entry import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.lang.FileASTNode +import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.psiUtil.leaves import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -1096,7 +1098,7 @@ class ASTNodeExtensionTest { val code = """ class MyClass { - fun foo() = 42 + fun foo() = 42 } """.trimIndent() val result = @@ -1110,7 +1112,7 @@ class ASTNodeExtensionTest { val code = """ class MyClass { - + } """.trimIndent() val result = @@ -1120,6 +1122,24 @@ class ASTNodeExtensionTest { } } + @Test + fun `Given a simple class declaration without body then the declaration itself is derived from KtAnnotated while its child elements are not derived from KtAnnotated`() { + val code = + """ + class Foo + """.trimIndent() + + val actual = transformCodeToAST(code).findChildByType(CLASS)!! + + assertThat(actual.isKtAnnotated()).isTrue() + assertThat(actual.findChildByType(CLASS_KEYWORD)!!.isKtAnnotated()).isFalse() + assertThat(actual.findChildByType(IDENTIFIER)!!.isKtAnnotated()).isFalse() + + assertThat(actual.isPsiType()).isTrue() + assertThat(actual.findChildByType(CLASS_KEYWORD)!!.isPsiType()).isFalse() + assertThat(actual.findChildByType(IDENTIFIER)!!.isPsiType()).isFalse() + } + private inline fun String.transformAst(block: FileASTNode.() -> Unit): FileASTNode = transformCodeToAST(this) .apply(block) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt index ff4ac50c4a..9d59cf71ee 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt @@ -7,16 +7,14 @@ import com.pinterest.ktlint.rule.engine.core.api.Rule import com.pinterest.ktlint.rule.engine.core.api.RuleId import com.pinterest.ktlint.rule.engine.core.api.TokenSets import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig +import com.pinterest.ktlint.rule.engine.core.api.isPsiType import com.pinterest.ktlint.rule.engine.core.api.nextSibling import com.pinterest.ktlint.rule.engine.core.api.parent import com.pinterest.ktlint.rule.engine.core.api.recursiveChildren import com.pinterest.ktlint.rule.engine.internal.SuppressionLocator.CommentSuppressionHint.Type.BLOCK_END import com.pinterest.ktlint.rule.engine.internal.SuppressionLocator.CommentSuppressionHint.Type.BLOCK_START import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.psi.KtAnnotated -import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementType internal class SuppressionLocator( editorConfig: EditorConfig, @@ -186,7 +184,8 @@ internal class SuppressionLocator( if (suppressedRuleIds.isEmpty()) return null - val owner = parent { it.canBeAnnotated() } ?: return null +// val owner = parent { it.isKtAnnotated() } ?: return null + val owner = parent { it.isPsiType() } ?: return null val textRange = owner.textRange @@ -265,27 +264,5 @@ internal class SuppressionLocator( ) val SUPPRESS_ANNOTATIONS = setOf("Suppress", "SuppressWarnings") const val ALL_KTLINT_RULES_SUPPRESSION_ID = "ktlint:suppress-all-rules" - - private fun ASTNode.canBeAnnotated(): Boolean = - when (val elementType = elementType) { - ElementType.FILE -> { - true - } - - ElementType.ANNOTATED_EXPRESSION -> { - true - } - - else -> { - DUMMY_PSI_ELEMENTS.getOrPut(elementType) { - when (elementType) { - is KtStubElementType<*, *> -> elementType.createPsiFromAst(this) - else -> throw NotImplementedError("getting dummy psi for $elementType (${elementType::class}) not implemented") - } - } is KtAnnotated - } - } - - private val DUMMY_PSI_ELEMENTS = hashMapOf() } } From 2c7c9e8b924ea8aada2049f1dc27501132079fd9 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Wed, 1 Jan 2025 21:15:42 +0100 Subject: [PATCH 25/27] Respect max line length in kdoc --- .../pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index eea3b28512..4c9096d559 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -588,7 +588,8 @@ public fun ASTNode.remove() { /** * Searches the receiver [ASTNode] recursively, returning the first child with type [elementType]. If none are found, returns [null]. - * If [includeSelf] is [true], includes the receiver in the search. The receiver would then be the first element searched, so it is guaranteed to be returned if it has type [elementType]. + * If [includeSelf] is [true], includes the receiver in the search. The receiver would then be the first element searched, so it is + * guaranteed to be returned if it has type [elementType]. */ public fun ASTNode.findChildByTypeRecursively( elementType: IElementType, From 72fb3c0ead6008d17dd28770dc47c61273dddb51 Mon Sep 17 00:00:00 2001 From: mgroth0 Date: Wed, 1 Jan 2025 18:33:55 -0500 Subject: [PATCH 26/27] clean up alternative psi-type-checking strategy and remove getPsi() calls --- .../rule/engine/core/api/ASTNodeExtension.kt | 98 +++++++++++++++---- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 4c9096d559..940fb46ed5 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -9,16 +9,29 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VAR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE import org.jetbrains.kotlin.KtNodeType import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.lang.FileASTNode +import org.jetbrains.kotlin.com.intellij.lang.Language +import org.jetbrains.kotlin.com.intellij.mock.MockProject +import org.jetbrains.kotlin.com.intellij.openapi.Disposable +import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.com.intellij.psi.AbstractFileViewProvider +import org.jetbrains.kotlin.com.intellij.psi.FileViewProvider import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.com.intellij.psi.PsiFile +import org.jetbrains.kotlin.com.intellij.psi.PsiReference +import org.jetbrains.kotlin.com.intellij.psi.impl.PsiManagerImpl import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.com.intellij.testFramework.LightVirtualFile +import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.lexer.KtKeywordToken import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.leaves import org.jetbrains.kotlin.psi.stubs.elements.KtFileElementType import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementType @@ -609,20 +622,7 @@ private val elementTypeCache = hashMapOf() */ public fun ASTNode.isKtAnnotated(): Boolean = psiType { it is KtAnnotated } -private inline fun ASTNode.psiType(predicate: (psiElement: PsiElement) -> Boolean): Boolean = - elementTypeCache - .getOrPut(elementType) { - // Create a dummy Psi element based on the current node, so that we can store the Psi Type for this ElementType. - // Creating this cache entry once per elementType is cheaper than accessing the psi for every node. - when (elementType) { - is KtFileElementType -> this.psi - is KtKeywordToken -> this.psi - is KtNodeType -> (elementType as KtNodeType).createPsi(this) - is KtStubElementType<*, *> -> (elementType as KtStubElementType<*, *>).createPsiFromAst(this) - is KtToken -> this.psi - else -> throw NotImplementedError("Cannot create dummy psi for $elementType (${elementType::class})") - } - }.let { predicate(it) } +private inline fun ASTNode.psiType(predicate: (psiElement: PsiElement) -> Boolean): Boolean = predicate(dummyPsiElement()) /** * Checks if the [AstNode] extends the [T] interface which implements [KtElement]. Call this function like: @@ -644,11 +644,75 @@ public fun ASTNode.dummyPsiElement(): PsiElement = // Create a dummy Psi element based on the current node, so that we can store the Psi Type for this ElementType. // Creating this cache entry once per elementType is cheaper than accessing the psi for every node. when (elementType) { - is KtFileElementType -> this.psi - is KtKeywordToken -> this.psi + is KtFileElementType -> KtFile(dummyFileViewProvider, false) + is KtKeywordToken -> this as PsiElement is KtNodeType -> (elementType as KtNodeType).createPsi(this) is KtStubElementType<*, *> -> (elementType as KtStubElementType<*, *>).createPsiFromAst(this) - is KtToken -> this.psi + is KtToken -> this as PsiElement else -> throw NotImplementedError("Cannot create dummy psi for $elementType (${elementType::class})") } } + +private val dummyFileViewProvider by lazy { + object : AbstractFileViewProvider( + PsiManagerImpl( + MockProject( + null, + object : Disposable { + override fun dispose() { + TODO("Not yet implemented") + } + }, + ), + ), + LightVirtualFile(), + false, + ) { + override fun getPsiInner(p0: Language?): PsiFile? { + TODO("Not yet implemented") + } + + override fun getCachedPsi(p0: Language): PsiFile? { + TODO("Not yet implemented") + } + + override fun getCachedPsiFiles(): List { + TODO("Not yet implemented") + } + + override fun getKnownTreeRoots(): List { + TODO("Not yet implemented") + } + + override fun getBaseLanguage(): Language { + TODO("Not yet implemented") + } + + private val languages = setOf(KotlinLanguage.INSTANCE) + + override fun getLanguages(): Set = languages + + override fun getAllFiles(): List { + TODO("Not yet implemented") + } + + override fun findElementAt(p0: Int): PsiElement? { + TODO("Not yet implemented") + } + + override fun findReferenceAt(p0: Int): PsiReference? { + TODO("Not yet implemented") + } + + override fun findElementAt( + p0: Int, + p1: Class, + ): PsiElement? { + TODO("Not yet implemented") + } + + override fun createCopy(p0: VirtualFile): FileViewProvider { + TODO("Not yet implemented") + } + } +} From 2ecce766b75ae0ab6dc4be681d158c666996a032 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Thu, 2 Jan 2025 17:57:22 +0100 Subject: [PATCH 27/27] Create dummy KtFile with PsiFileFactory analog to KotlinPsiFileFactory The KotlinPsiFileFactory is not reused it is meant to be kept internal. --- .../rule/engine/core/api/ASTNodeExtension.kt | 101 +++++------------- 1 file changed, 27 insertions(+), 74 deletions(-) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt index 940fb46ed5..2722e63676 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt @@ -8,24 +8,19 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.VARARG_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.VAR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE import org.jetbrains.kotlin.KtNodeType +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.lang.FileASTNode -import org.jetbrains.kotlin.com.intellij.lang.Language import org.jetbrains.kotlin.com.intellij.mock.MockProject -import org.jetbrains.kotlin.com.intellij.openapi.Disposable -import org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFile -import org.jetbrains.kotlin.com.intellij.psi.AbstractFileViewProvider -import org.jetbrains.kotlin.com.intellij.psi.FileViewProvider +import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer import org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.com.intellij.psi.PsiFile -import org.jetbrains.kotlin.com.intellij.psi.PsiReference -import org.jetbrains.kotlin.com.intellij.psi.impl.PsiManagerImpl +import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet -import org.jetbrains.kotlin.com.intellij.testFramework.LightVirtualFile +import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.lexer.KtKeywordToken import org.jetbrains.kotlin.lexer.KtToken @@ -600,8 +595,8 @@ public fun ASTNode.remove() { } /** - * Searches the receiver [ASTNode] recursively, returning the first child with type [elementType]. If none are found, returns [null]. - * If [includeSelf] is [true], includes the receiver in the search. The receiver would then be the first element searched, so it is + * Searches the receiver [ASTNode] recursively, returning the first child with type [elementType]. If none are found, returns `null`. + * If [includeSelf] is `true`, includes the receiver in the search. The receiver would then be the first element searched, so it is * guaranteed to be returned if it has type [elementType]. */ public fun ASTNode.findChildByTypeRecursively( @@ -644,7 +639,7 @@ public fun ASTNode.dummyPsiElement(): PsiElement = // Create a dummy Psi element based on the current node, so that we can store the Psi Type for this ElementType. // Creating this cache entry once per elementType is cheaper than accessing the psi for every node. when (elementType) { - is KtFileElementType -> KtFile(dummyFileViewProvider, false) + is KtFileElementType -> createDummyKtFile() is KtKeywordToken -> this as PsiElement is KtNodeType -> (elementType as KtNodeType).createPsi(this) is KtStubElementType<*, *> -> (elementType as KtStubElementType<*, *>).createPsiFromAst(this) @@ -653,66 +648,24 @@ public fun ASTNode.dummyPsiElement(): PsiElement = } } -private val dummyFileViewProvider by lazy { - object : AbstractFileViewProvider( - PsiManagerImpl( - MockProject( - null, - object : Disposable { - override fun dispose() { - TODO("Not yet implemented") - } - }, - ), - ), - LightVirtualFile(), - false, - ) { - override fun getPsiInner(p0: Language?): PsiFile? { - TODO("Not yet implemented") - } - - override fun getCachedPsi(p0: Language): PsiFile? { - TODO("Not yet implemented") - } - - override fun getCachedPsiFiles(): List { - TODO("Not yet implemented") - } - - override fun getKnownTreeRoots(): List { - TODO("Not yet implemented") - } - - override fun getBaseLanguage(): Language { - TODO("Not yet implemented") - } - - private val languages = setOf(KotlinLanguage.INSTANCE) - - override fun getLanguages(): Set = languages - - override fun getAllFiles(): List { - TODO("Not yet implemented") - } - - override fun findElementAt(p0: Int): PsiElement? { - TODO("Not yet implemented") - } - - override fun findReferenceAt(p0: Int): PsiReference? { - TODO("Not yet implemented") - } - - override fun findElementAt( - p0: Int, - p1: Class, - ): PsiElement? { - TODO("Not yet implemented") - } - - override fun createCopy(p0: VirtualFile): FileViewProvider { - TODO("Not yet implemented") - } +private fun createDummyKtFile(): KtFile { + val disposable = Disposer.newDisposable() + try { + val project = + KotlinCoreEnvironment + .createForProduction( + disposable, + CompilerConfiguration(), + EnvironmentConfigFiles.JVM_CONFIG_FILES, + ).project as MockProject + + return PsiFileFactory + .getInstance(project) + .createFileFromText("dummy-file.kt", KotlinLanguage.INSTANCE, "") as KtFile + } finally { + // Dispose explicitly to (possibly) prevent memory leak + // https://discuss.kotlinlang.org/t/memory-leak-in-kotlincoreenvironment-and-kotlintojvmbytecodecompiler/21950 + // https://youtrack.jetbrains.com/issue/KT-47044 + disposable.dispose() } }