diff --git a/detekt-compiler-plugin/gradle.properties b/detekt-compiler-plugin/gradle.properties index f7002ab484e..4598e283428 100644 --- a/detekt-compiler-plugin/gradle.properties +++ b/detekt-compiler-plugin/gradle.properties @@ -1,4 +1,4 @@ -kotlinCompilerChecksum=93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88 +kotlinCompilerChecksum=eb7b68e01029fa67bc8d060ee54c12018f2c60ddc438cf21db14517229aa693b kotlin.code.style=official systemProp.sonar.host.url=http://localhost:9000 diff --git a/detekt-psi-utils/api/detekt-psi-utils.api b/detekt-psi-utils/api/detekt-psi-utils.api index baa63427fa7..86273493064 100644 --- a/detekt-psi-utils/api/detekt-psi-utils.api +++ b/detekt-psi-utils/api/detekt-psi-utils.api @@ -42,10 +42,6 @@ public final class io/gitlab/arturbosch/detekt/rules/IsPartOfUtilsKt { public final class io/gitlab/arturbosch/detekt/rules/JunkKt { public static final fun companionObject (Lorg/jetbrains/kotlin/psi/KtClass;)Lorg/jetbrains/kotlin/psi/KtObjectDeclaration; - public static final fun getIntValueForPsiElement (Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;)Ljava/lang/Integer; - public static final fun hasCommentInside (Lorg/jetbrains/kotlin/com/intellij/psi/PsiElement;)Z - public static final fun hasCommentInside (Lorg/jetbrains/kotlin/psi/KtClassOrObject;)Z - public static final fun isUsedForNesting (Lorg/jetbrains/kotlin/psi/KtCallExpression;)Z public static final fun receiverIsUsed (Lorg/jetbrains/kotlin/psi/KtCallExpression;Lorg/jetbrains/kotlin/resolve/BindingContext;)Z } diff --git a/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Junk.kt b/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Junk.kt index a8bdcc1fdea..9245e07924b 100644 --- a/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Junk.kt +++ b/detekt-psi-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/Junk.kt @@ -1,40 +1,12 @@ package io.gitlab.arturbosch.detekt.rules -import org.jetbrains.kotlin.com.intellij.openapi.util.Key -import org.jetbrains.kotlin.com.intellij.psi.PsiComment -import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtConstantExpression import org.jetbrains.kotlin.psi.KtQualifiedExpression -import org.jetbrains.kotlin.psi.KtTreeVisitorVoid -import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression -fun KtCallExpression.isUsedForNesting(): Boolean = when (getCallNameExpression()?.text) { - "run", "let", "apply", "with", "use", "forEach" -> true - else -> false -} - -fun KtClassOrObject.hasCommentInside() = this.body?.hasCommentInside() ?: false - -fun PsiElement.hasCommentInside(): Boolean { - val commentKey = Key("comment") - this.acceptChildren(object : KtTreeVisitorVoid() { - override fun visitComment(comment: PsiComment) { - putUserData(commentKey, true) - } - }) - return getUserData(commentKey) == true -} - -fun getIntValueForPsiElement(element: PsiElement): Int? { - return (element as? KtConstantExpression)?.text?.toIntOrNull() -} - fun KtClass.companionObject() = this.companionObjects.singleOrNull { it.isCompanion() } fun KtCallExpression.receiverIsUsed(context: BindingContext): Boolean = diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedBlockDepth.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedBlockDepth.kt index a5e7f50bdae..448ba30522a 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedBlockDepth.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedBlockDepth.kt @@ -7,7 +7,6 @@ import io.gitlab.arturbosch.detekt.api.DetektVisitor import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.config -import io.gitlab.arturbosch.detekt.rules.isUsedForNesting import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody import org.jetbrains.kotlin.psi.KtIfExpression @@ -16,6 +15,7 @@ import org.jetbrains.kotlin.psi.KtLoopExpression import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtTryExpression import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression /** * This rule reports excessive nesting depth in functions. Excessively nested code becomes harder to read and increases @@ -110,5 +110,8 @@ class NestedBlockDepth(config: Config) : Rule( } } } + + private fun KtCallExpression.isUsedForNesting(): Boolean = + getCallNameExpression()?.text in setOf("run", "let", "apply", "with", "use", "forEach") } } diff --git a/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyClassBlock.kt b/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyClassBlock.kt index 117900462a9..00698a6a6b6 100644 --- a/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyClassBlock.kt +++ b/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyClassBlock.kt @@ -4,7 +4,7 @@ import io.gitlab.arturbosch.detekt.api.ActiveByDefault import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Entity -import io.gitlab.arturbosch.detekt.rules.hasCommentInside +import io.gitlab.arturbosch.detekt.rules.empty.internal.hasCommentInside import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral diff --git a/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyRule.kt b/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyRule.kt index 3aa4a70d6a5..7ed10e6d32c 100644 --- a/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyRule.kt +++ b/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/EmptyRule.kt @@ -4,7 +4,7 @@ import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.rules.hasCommentInside +import io.gitlab.arturbosch.detekt.rules.empty.internal.hasCommentInside import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.psi.KtBlockExpression diff --git a/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/internal/ContainsComments.kt b/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/internal/ContainsComments.kt new file mode 100644 index 00000000000..84188aeda03 --- /dev/null +++ b/detekt-rules-empty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/empty/internal/ContainsComments.kt @@ -0,0 +1,10 @@ +package io.gitlab.arturbosch.detekt.rules.empty.internal + +import org.jetbrains.kotlin.com.intellij.psi.PsiComment +import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType + +fun KtClassOrObject.hasCommentInside() = this.body?.hasCommentInside() ?: false + +fun PsiElement.hasCommentInside(): Boolean = getChildrenOfType().isNotEmpty() diff --git a/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/InvalidRange.kt b/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/InvalidRange.kt index b49373c73c4..ee37002662d 100644 --- a/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/InvalidRange.kt +++ b/detekt-rules-errorprone/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/bugs/InvalidRange.kt @@ -5,9 +5,8 @@ import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule -import io.gitlab.arturbosch.detekt.rules.getIntValueForPsiElement -import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtConstantExpression /** * Reports ranges which are empty. @@ -35,38 +34,21 @@ class InvalidRange(config: Config) : Rule( "If a for loops condition is false before the first iteration, the loop will never get executed." ) { - private val minimumSize = 3 - override fun visitBinaryExpression(expression: KtBinaryExpression) { - val range = expression.children - if (range.size >= minimumSize && hasInvalidLoopRange(range)) { - report( - CodeSmell( - Entity.from(expression), - "This loop will never be executed due to its expression." - ) - ) + if (expression.isInvalidLoopRange()) { + report(CodeSmell(Entity.from(expression), "This loop will never be executed due to its expression.")) } super.visitBinaryExpression(expression) } - private fun hasInvalidLoopRange(range: Array): Boolean { - val lowerValue = getIntValueForPsiElement(range[0]) - val upperValue = getIntValueForPsiElement(range[2]) - if (lowerValue == null || upperValue == null) { - return false - } - return when (range[1].text) { - ".." -> checkRangeTo(lowerValue, upperValue) - "downTo" -> checkDownTo(lowerValue, upperValue) - "until", "..<" -> checkUntil(lowerValue, upperValue) + private fun KtBinaryExpression.isInvalidLoopRange(): Boolean { + val lower = (left as? KtConstantExpression)?.text?.toIntOrNull() ?: return false + val upper = (right as? KtConstantExpression)?.text?.toIntOrNull() ?: return false + return when (operationReference.text) { + ".." -> lower > upper + "downTo" -> lower < upper + "until", "..<" -> lower >= upper else -> false } } - - private fun checkRangeTo(lower: Int, upper: Int) = lower > upper - - private fun checkDownTo(lower: Int, upper: Int) = lower < upper - - private fun checkUntil(lower: Int, upper: Int) = lower >= upper } diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/Junk.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/Junk.kt index 30530eb4b3f..60de974ec4f 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/Junk.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/Junk.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType * the given [line] from a given offset in a [KtFile]. */ internal fun findKtElementInParents(file: KtFile, offset: Int, line: String): Sequence { - return file.elementsInRange(TextRange.create(offset - line.length, offset)) + return file.elementsInRange(TextRange.create(offset, offset + line.length)) .asSequence() .plus(file.findElementAt(offset)) .mapNotNull { it?.getNonStrictParentOfType() } diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLength.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLength.kt index afd5be03d29..06bdd28e7b5 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLength.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/MaxLineLength.kt @@ -1,5 +1,6 @@ package io.gitlab.arturbosch.detekt.rules.style +import io.github.detekt.psi.absolutePath import io.gitlab.arturbosch.detekt.api.ActiveByDefault import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config @@ -13,10 +14,13 @@ import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.rules.lastArgumentMatchesKotlinReferenceUrlSyntax import io.gitlab.arturbosch.detekt.rules.lastArgumentMatchesMarkdownUrlSyntax import io.gitlab.arturbosch.detekt.rules.lastArgumentMatchesUrl +import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping +import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.diagnostics.DiagnosticUtils.getLineAndColumnRangeInPsiFile import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtStringTemplateExpression -import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType /** * This rule reports lines of code which exceed a defined maximum line length. @@ -48,26 +52,26 @@ class MaxLineLength(config: Config) : Rule( override fun visitKtFile(file: KtFile) { super.visitKtFile(file) - var offset = 0 - val lines = file.text.lines() - for (line in lines) { - offset += line.length - if (!isValidLine(file, offset, line)) { + val sourceFileLinesMapping = KtPsiSourceFileLinesMapping(file) + + file.text.lines().withIndex() + .filterNot { isValidLine(file, sourceFileLinesMapping.getLineStartOffset(it.index), it.value) } + .forEach { + val offset = sourceFileLinesMapping.getLineStartOffset(it.index) + val line = it.value val ktElement = findFirstMeaningfulKtElementInParents(file, offset, line) ?: file - val location = Location.from(file, offset - line.length).let { location -> + val textRange = TextRange(offset, offset + line.length) + val lineAndColumnRange = getLineAndColumnRangeInPsiFile(file, textRange) + val location = Location( - source = location.source, - endSource = SourceLocation(location.source.line, line.length + 1), - text = TextLocation(offset - line.length, offset), - path = location.path, + source = SourceLocation(lineAndColumnRange.start.line, lineAndColumnRange.start.column), + endSource = SourceLocation(lineAndColumnRange.end.line, lineAndColumnRange.end.column), + text = TextLocation(offset, offset + line.length), + path = file.absolutePath(), ) - } report(CodeSmell(Entity.from(ktElement, location), description)) } - - offset += 1 // '\n' - } } private fun isValidLine(file: KtFile, offset: Int, line: String): Boolean { @@ -123,5 +127,5 @@ class MaxLineLength(config: Config) : Rule( } private fun PsiElement.isInsideRawString(): Boolean { - return this is KtStringTemplateExpression || getParentOfType(false) != null + return this is KtStringTemplateExpression || getNonStrictParentOfType() != null } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b4f0f79367f..7ee35240231 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] dokka = "1.9.20" jacoco = "0.8.12" -kotlin = "1.9.23" +kotlin = "1.9.24" coroutines = "1.8.0" ktlint = "1.2.1" junit = "5.10.2"