From 236635acd2635f484289b4646c006db54aaa197f Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Wed, 8 May 2024 22:19:18 +1000 Subject: [PATCH] Rewrite MaxLineLength & TrailingWhitespace to avoid problematic offset calcs (#7270) * Ensure positive integers passed when finding elements in provided range Negative values are invalid as the text range must start and end within the range 0 to file.text.length * Rewrite MaxLineLength to avoid problematic offset calcs * Tidy up * Improve readability * Rewrite TrailingWhitespace to avoid problematic offset calcs --- .../arturbosch/detekt/rules/style/Junk.kt | 2 +- .../detekt/rules/style/MaxLineLength.kt | 35 ++++++++++-------- .../detekt/rules/style/TrailingWhitespace.kt | 37 +++++++++++-------- 3 files changed, 41 insertions(+), 33 deletions(-) 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..c9ce65f4cd0 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,25 @@ 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 { (index, line) -> isValidLine(file, sourceFileLinesMapping.getLineStartOffset(index), line) } + .forEach { (index, line) -> + val offset = sourceFileLinesMapping.getLineStartOffset(index) 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 +126,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/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrailingWhitespace.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrailingWhitespace.kt index 33a22aa94d4..7bc1f10fc29 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrailingWhitespace.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/TrailingWhitespace.kt @@ -1,13 +1,18 @@ package io.gitlab.arturbosch.detekt.rules.style +import io.github.detekt.psi.absolutePath 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.Location import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.SourceLocation import io.gitlab.arturbosch.detekt.api.TextLocation import io.gitlab.arturbosch.detekt.rules.isPartOfString +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 /** @@ -26,29 +31,29 @@ class TrailingWhitespace(config: Config) : Rule( override fun visitKtFile(file: KtFile) { super.visitKtFile(file) - var offset = 0 + + val sourceFileLinesMapping = KtPsiSourceFileLinesMapping(file) + file.text.lineSequence().forEachIndexed { index, line -> - offset += line.length val trailingWhitespaces = countTrailingWhitespace(line) if (trailingWhitespaces > 0) { - val ktElement = findFirstKtElementInParentsOrNull(file, offset, line) + val lineEndOffset = sourceFileLinesMapping.getLineStartOffset(index) + line.length + val ktElement = findFirstKtElementInParentsOrNull(file, lineEndOffset, line) if (ktElement == null || !ktElement.isPartOfString()) { - val entity = Entity.from(file, offset - trailingWhitespaces).let { entity -> - Entity( - entity.name, - entity.signature, - location = Location( - entity.location.source, - entity.location.endSource, - TextLocation(entity.location.text.start, offset), - entity.location.path - ) + val startOffset = lineEndOffset - trailingWhitespaces + val textRange = TextRange(startOffset, lineEndOffset) + val lineAndColumnRange = getLineAndColumnRangeInPsiFile(file, textRange) + val location = + Location( + source = SourceLocation(lineAndColumnRange.start.line, lineAndColumnRange.start.column), + endSource = SourceLocation(lineAndColumnRange.end.line, lineAndColumnRange.end.column), + text = TextLocation(startOffset, lineEndOffset), + path = file.absolutePath(), ) - } - report(CodeSmell(entity, createMessage(index))) + + report(CodeSmell(Entity.from(file, location), createMessage(index))) } } - offset += 1 // '\n' } }