From ba6a690b178f2618f473207e929a5f46f57f3928 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 12:21:19 +0300 Subject: [PATCH 01/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 3a69210..f195eba 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -44,10 +44,11 @@ class SarifFixAdapter( val processedFiles = sarifSchema210.runs.asSequence().flatMapIndexed { index, run -> val runReplacements: List = extractFixObjects(run) if (runReplacements.isEmpty()) { - println("Run #$index have no `fix object` section!") + println("Run #$index have no any `fix object` section!") emptyList() } else { - applyReplacementsToFiles(runReplacements, targetFiles) + val groupedReplacements = groupReplacementsByFiles(runReplacements) + applyReplacementsToFiles(groupedReplacements, targetFiles) } } return processedFiles.toList() @@ -88,54 +89,64 @@ class SarifFixAdapter( ?.toList() ?: emptyList() } + private fun groupReplacementsByFiles(runReplacements: List): List { + // flat replacements by all rules into single list and group by file path + return runReplacements.flatten().groupBy { fileReplacements -> + fileReplacements.filePath + }.map { entry -> + // now collect all replacements by all rules for each file into single instance of `FileReplacements` + val filePath = entry.key + val fileReplacements = entry.value + FileReplacements( + filePath, + fileReplacements.flatMap { + it.replacements + } + ) + } + } + + /** * Apply fixes from single run to the target files * - * @param runReplacements list of replacements from all rules + * @param fileReplacementsList list of replacements from all rules * @param targetFiles list of target files */ - private fun applyReplacementsToFiles(runReplacements: List, targetFiles: List): List = runReplacements.flatMap { ruleReplacements -> - val filteredRuleReplacements = filterRuleReplacements(ruleReplacements) - filteredRuleReplacements.mapNotNull { fileReplacements -> - val targetFile = targetFiles.find { - val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { - fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) + private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { + val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) + return filteredRuleReplacements.mapNotNull { fileReplacements -> + val targetFile = targetFiles.find { + val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { + fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) + } else { + fileReplacements.filePath + } + fs.canonicalize(it) == fullPathOfFileFromSarif + } + if (targetFile == null) { + println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") + null } else { - fileReplacements.filePath + applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) } - fs.canonicalize(it) == fullPathOfFileFromSarif } - if (targetFile == null) { - println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") - null - } else { - applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) - } - } } /** * If there are several fixes in one file on the same line by different rules, take only the first one * - * @param ruleReplacements list of replacements by all rules + * @param fileReplacementsList list of replacements by all rules * @return filtered list of replacements by all rules */ - private fun filterRuleReplacements(ruleReplacements: RuleReplacements): RuleReplacements { - // group replacements for each file by all rules - val listOfAllReplacementsForEachFile = ruleReplacements.groupBy { fileReplacement -> - fileReplacement.filePath.toString() - }.values - + private fun filterRuleReplacements(fileReplacementsList: List): List { // distinct replacements from all rules for each file by `startLine`, // i.e., take only first of possible fixes for each line - return listOfAllReplacementsForEachFile.map { fileReplacementsListForSingleFile -> + return fileReplacementsList.map { fileReplacementsListForSingleFile -> // since we already grouped replacements by file path, it will equal for all elements in list, can take first of them - val filePath = fileReplacementsListForSingleFile.first().filePath + val filePath = fileReplacementsListForSingleFile.filePath - val distinctReplacements = fileReplacementsListForSingleFile.flatMap { fileReplacements -> - // map fixes from different rules into single list - fileReplacements.replacements - }.groupBy { + val distinctReplacements = fileReplacementsListForSingleFile.replacements.groupBy { // group all fixes for current file by startLine it.deletedRegion.startLine }.flatMap { entry -> From fbf9fa90a8608aa339af82277083a95260ae00c8 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 12:42:17 +0300 Subject: [PATCH 02/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 102 +++++++++--------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index f195eba..4821d9a 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -106,33 +106,6 @@ class SarifFixAdapter( } } - - /** - * Apply fixes from single run to the target files - * - * @param fileReplacementsList list of replacements from all rules - * @param targetFiles list of target files - */ - private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { - val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) - return filteredRuleReplacements.mapNotNull { fileReplacements -> - val targetFile = targetFiles.find { - val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { - fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) - } else { - fileReplacements.filePath - } - fs.canonicalize(it) == fullPathOfFileFromSarif - } - if (targetFile == null) { - println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") - null - } else { - applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) - } - } - } - /** * If there are several fixes in one file on the same line by different rules, take only the first one * @@ -140,12 +113,10 @@ class SarifFixAdapter( * @return filtered list of replacements by all rules */ private fun filterRuleReplacements(fileReplacementsList: List): List { - // distinct replacements from all rules for each file by `startLine`, + // distinct replacements for each file by `startLine`, // i.e., take only first of possible fixes for each line return fileReplacementsList.map { fileReplacementsListForSingleFile -> - // since we already grouped replacements by file path, it will equal for all elements in list, can take first of them val filePath = fileReplacementsListForSingleFile.filePath - val distinctReplacements = fileReplacementsListForSingleFile.replacements.groupBy { // group all fixes for current file by startLine it.deletedRegion.startLine @@ -158,10 +129,9 @@ class SarifFixAdapter( " Only the first fix will be applied") } replacementsList + }.distinctBy { + it.deletedRegion.startLine } - .distinctBy { - it.deletedRegion.startLine - } FileReplacements( filePath, distinctReplacements @@ -169,6 +139,32 @@ class SarifFixAdapter( } } + /** + * Apply fixes from single run to the target files + * + * @param fileReplacementsList list of replacements from all rules + * @param targetFiles list of target files + */ + private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { + val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) + return filteredRuleReplacements.mapNotNull { fileReplacements -> + val targetFile = targetFiles.find { + val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { + fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) + } else { + fileReplacements.filePath + } + fs.canonicalize(it) == fullPathOfFileFromSarif + } + if (targetFile == null) { + println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") + null + } else { + applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) + } + } + } + /** * Create copy of the target file and apply fixes from sarif * @@ -179,12 +175,8 @@ class SarifFixAdapter( @Suppress("TOO_LONG_FUNCTION") private fun applyReplacementsToSingleFile(targetFile: Path, replacements: List): Path { val targetFileCopy = tmpDir.resolve(targetFile.name) - // If file doesn't exist, fill it with original data - // Otherwise, that's mean, that we already made some changes to it (by other rules), - // so continue to work with modified file - if (!fs.exists(targetFileCopy)) { - fs.copy(targetFile, targetFileCopy) - } + fs.copy(targetFile, targetFileCopy) + val fileContent = readLines(targetFileCopy).toMutableList() replacements.forEach { replacement -> @@ -197,20 +189,7 @@ class SarifFixAdapter( } val insertedContent = replacement.insertedContent?.text - insertedContent?.let { - if (startColumn != null && endColumn != null) { - fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) - } else { - fileContent[startLine] = it - } - } ?: run { - if (startColumn != null && endColumn != null) { - fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) - } else { - // remove all content but leave empty line, until we support https://github.com/saveourtool/sarif-utils/issues/13 - fileContent[startLine] = "\n" - } - } + applyFixToLine(fileContent, insertedContent, startLine, startColumn, endColumn) } fs.write(targetFileCopy) { fileContent.forEach { line -> @@ -220,3 +199,20 @@ class SarifFixAdapter( return targetFileCopy } } + +private fun applyFixToLine(fileContent: MutableList, insertedContent: String?, startLine: Int, startColumn: Int?, endColumn: Int?) { + insertedContent?.let { + if (startColumn != null && endColumn != null) { + fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) + } else { + fileContent[startLine] = it + } + } ?: run { + if (startColumn != null && endColumn != null) { + fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) + } else { + // remove all content but leave empty line, until we support https://github.com/saveourtool/sarif-utils/issues/13 + fileContent[startLine] = "\n" + } + } +} From a9b616c4d9c3a1041cc16bffd982629d6df176b0 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 12:54:23 +0300 Subject: [PATCH 03/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 4821d9a..5b604cc 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -89,6 +89,12 @@ class SarifFixAdapter( ?.toList() ?: emptyList() } + /** + * Group all replacements from all rules by file name + * + * @param runReplacements list of replacements by all rules + * @return list of [FileReplacements], where [replacements] field contain replacements from all rules + */ private fun groupReplacementsByFiles(runReplacements: List): List { // flat replacements by all rules into single list and group by file path return runReplacements.flatten().groupBy { fileReplacements -> @@ -198,21 +204,30 @@ class SarifFixAdapter( } return targetFileCopy } -} -private fun applyFixToLine(fileContent: MutableList, insertedContent: String?, startLine: Int, startColumn: Int?, endColumn: Int?) { - insertedContent?.let { - if (startColumn != null && endColumn != null) { - fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) - } else { - fileContent[startLine] = it - } - } ?: run { - if (startColumn != null && endColumn != null) { - fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) - } else { - // remove all content but leave empty line, until we support https://github.com/saveourtool/sarif-utils/issues/13 - fileContent[startLine] = "\n" + /** + * Apply single line fixes into [fileContent] + * + * @param fileContent file data, where need to change content + * @param insertedContent represents inserted content into the line from [fileContent] with index [startLine], or null if fix represent the deletion of region + * @param startLine index of line, which need to be changed + * @param startColumn index of column, starting from which content should be changed, or null if [startLine] will be completely replaced + * @param endColumn index of column, ending with which content should be changed, or null if [startLine] will be completely replaced + */ + private fun applyFixToLine(fileContent: MutableList, insertedContent: String?, startLine: Int, startColumn: Int?, endColumn: Int?) { + insertedContent?.let { + if (startColumn != null && endColumn != null) { + fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) + } else { + fileContent[startLine] = it + } + } ?: run { + if (startColumn != null && endColumn != null) { + fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) + } else { + // remove all content but leave empty line, until we support https://github.com/saveourtool/sarif-utils/issues/13 + fileContent[startLine] = "\n" + } } } } From ae3473b9b3087b36ed1a34e0767e1ce138db1d5b Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 12:59:24 +0300 Subject: [PATCH 04/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 5b604cc..91ecd8a 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -152,23 +152,24 @@ class SarifFixAdapter( * @param targetFiles list of target files */ private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { - val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) - return filteredRuleReplacements.mapNotNull { fileReplacements -> - val targetFile = targetFiles.find { - val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { - fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) - } else { - fileReplacements.filePath - } - fs.canonicalize(it) == fullPathOfFileFromSarif - } - if (targetFile == null) { - println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") - null + // if there are several fixes by different rules on the same line for any file, take only first of them + val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) + return filteredRuleReplacements.mapNotNull { fileReplacements -> + val targetFile = targetFiles.find { + val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { + fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) } else { - applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) + fileReplacements.filePath } + fs.canonicalize(it) == fullPathOfFileFromSarif } + if (targetFile == null) { + println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") + null + } else { + applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) + } + } } /** @@ -214,7 +215,13 @@ class SarifFixAdapter( * @param startColumn index of column, starting from which content should be changed, or null if [startLine] will be completely replaced * @param endColumn index of column, ending with which content should be changed, or null if [startLine] will be completely replaced */ - private fun applyFixToLine(fileContent: MutableList, insertedContent: String?, startLine: Int, startColumn: Int?, endColumn: Int?) { + private fun applyFixToLine( + fileContent: MutableList, + insertedContent: String?, + startLine: Int, + startColumn: Int?, + endColumn: Int? + ) { insertedContent?.let { if (startColumn != null && endColumn != null) { fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) From 9ca4a30c71665797e11e35f1158f3a256cb4977c Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 15:52:36 +0300 Subject: [PATCH 05/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 91ecd8a..6522d5e 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -123,28 +123,50 @@ class SarifFixAdapter( // i.e., take only first of possible fixes for each line return fileReplacementsList.map { fileReplacementsListForSingleFile -> val filePath = fileReplacementsListForSingleFile.filePath - val distinctReplacements = fileReplacementsListForSingleFile.replacements.groupBy { - // group all fixes for current file by startLine - it.deletedRegion.startLine - }.flatMap { entry -> - val startLine = entry.key - val replacementsList = entry.value - - if (replacementsList.size > 1) { - println("Some of fixes for $filePath were ignored, due they refer to the same line: $startLine." + - " Only the first fix will be applied") + val sortedReplacements = fileReplacementsListForSingleFile.replacements.map { replacement -> + if (replacement.deletedRegion.endLine == null) { + val deletedRegion = replacement.deletedRegion.copy( + endLine = replacement.deletedRegion.startLine + ) + replacement.copy( + deletedRegion = deletedRegion + ) + } else { + replacement } - replacementsList - }.distinctBy { - it.deletedRegion.startLine - } + }.sortedWith( + compareBy( { it.deletedRegion.startLine }, {it.deletedRegion.endLine}) + ) + println("==========> ${sortedReplacements}") + val nonOverlapingFixes = getNonOverlapingReplacements(fileReplacementsListForSingleFile.filePath, sortedReplacements) + + println("------------------> ${nonOverlapingFixes}") + FileReplacements( filePath, - distinctReplacements + nonOverlapingFixes.reversed() ) } } + private fun getNonOverlapingReplacements(filePath: Path, sortedReplacements: List): MutableList { + val nonOverlapingFixes: MutableList = mutableListOf(sortedReplacements[0]) + var currentEndLine = sortedReplacements[0].deletedRegion.endLine!! + + for (i in 1 until sortedReplacements.size) { + if (sortedReplacements[i].deletedRegion.startLine!! <= currentEndLine) { + println("Fix ${sortedReplacements[i].prettyString()} for $filePath was ignored, due it overlaps with others." + + " Only the first fix for this region will be applied.") + } + else { + nonOverlapingFixes.add(sortedReplacements[i]) + currentEndLine = sortedReplacements[i].deletedRegion.endLine!! + } + } + return nonOverlapingFixes + } + + /** * Apply fixes from single run to the target files * @@ -237,4 +259,11 @@ class SarifFixAdapter( } } } + + + private fun Replacement.prettyString(): String { + return "(startLine: ${this.deletedRegion.startLine}, endLine: ${this.deletedRegion.endLine}, " + + "startColumn: ${this.deletedRegion.startColumn}, endColumn: ${this.deletedRegion.endColumn}, insertedContent: ${this.insertedContent})" + + } } From 25e716948652f41e6c88caea129d120ca478fba1 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:50:30 +0300 Subject: [PATCH 06/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 91 +++++++++++-------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 6522d5e..628682e 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -112,17 +112,48 @@ class SarifFixAdapter( } } + /** - * If there are several fixes in one file on the same line by different rules, take only the first one + * Apply fixes from single run to the target files + * + * @param fileReplacementsList list of replacements from all rules + * @param targetFiles list of target files + */ + private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { + // if there are several fixes by different rules for the same region within one file, take only first of them + // and reverse the order of replacements for each file + val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) + return filteredRuleReplacements.mapNotNull { fileReplacements -> + val targetFile = targetFiles.find { + val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { + fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) + } else { + fileReplacements.filePath + } + fs.canonicalize(it) == fullPathOfFileFromSarif + } + if (targetFile == null) { + println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") + null + } else { + applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) + } + } + } + + /** + * If there are several fixes in one file for the same region by different rules, take only the first one, + * also return filtered replacements in reverse order, in aim to provide fixes on the next stages without invalidation of indexes, + * i.e. in case of multiline fixes, which will add new lines, or remove regions, that is, shift all the lines below, + * apply all fixes, starting from the last fix, thus it won't break startLine's and endLine's for other fixes * * @param fileReplacementsList list of replacements by all rules * @return filtered list of replacements by all rules */ private fun filterRuleReplacements(fileReplacementsList: List): List { - // distinct replacements for each file by `startLine`, - // i.e., take only first of possible fixes for each line return fileReplacementsList.map { fileReplacementsListForSingleFile -> val filePath = fileReplacementsListForSingleFile.filePath + // sort replacements by startLine val sortedReplacements = fileReplacementsListForSingleFile.replacements.map { replacement -> if (replacement.deletedRegion.endLine == null) { val deletedRegion = replacement.deletedRegion.copy( @@ -137,20 +168,28 @@ class SarifFixAdapter( }.sortedWith( compareBy( { it.deletedRegion.startLine }, {it.deletedRegion.endLine}) ) - println("==========> ${sortedReplacements}") - val nonOverlapingFixes = getNonOverlapingReplacements(fileReplacementsListForSingleFile.filePath, sortedReplacements) - - println("------------------> ${nonOverlapingFixes}") + // now, from overlapping fixes, take only the first one + val nonOverlappingFixes = getNonOverlappingReplacements(fileReplacementsListForSingleFile.filePath, sortedReplacements) + // save replacements in reverse order FileReplacements( filePath, - nonOverlapingFixes.reversed() + nonOverlappingFixes.reversed() ) } } - private fun getNonOverlapingReplacements(filePath: Path, sortedReplacements: List): MutableList { - val nonOverlapingFixes: MutableList = mutableListOf(sortedReplacements[0]) + /** + * For the [sortedReplacements] list take only non overlapping replacements. + * Replacement overlaps, if they are fix the same region, e.g: max(fix1.startLine, fix2.startLine) <= min(fix1.endLine, fix2.endLine), + * or, for sorted intervals by startLine it's equals to (fix2.startLine <= fix1.endLine) + * + * @param filePath file path + * @param sortedReplacements list of replacements, sorted by [startLine] + * @return list of non overlapping replacements + */ + private fun getNonOverlappingReplacements(filePath: Path, sortedReplacements: List): MutableList { + val nonOverlappingFixes: MutableList = mutableListOf(sortedReplacements[0]) var currentEndLine = sortedReplacements[0].deletedRegion.endLine!! for (i in 1 until sortedReplacements.size) { @@ -159,39 +198,11 @@ class SarifFixAdapter( " Only the first fix for this region will be applied.") } else { - nonOverlapingFixes.add(sortedReplacements[i]) + nonOverlappingFixes.add(sortedReplacements[i]) currentEndLine = sortedReplacements[i].deletedRegion.endLine!! } } - return nonOverlapingFixes - } - - - /** - * Apply fixes from single run to the target files - * - * @param fileReplacementsList list of replacements from all rules - * @param targetFiles list of target files - */ - private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { - // if there are several fixes by different rules on the same line for any file, take only first of them - val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) - return filteredRuleReplacements.mapNotNull { fileReplacements -> - val targetFile = targetFiles.find { - val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { - fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) - } else { - fileReplacements.filePath - } - fs.canonicalize(it) == fullPathOfFileFromSarif - } - if (targetFile == null) { - println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") - null - } else { - applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) - } - } + return nonOverlappingFixes } /** From 303631f4e914237f1eecc58fd7f3eb645676f908 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:55:23 +0300 Subject: [PATCH 07/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 628682e..2876dea 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -154,23 +154,9 @@ class SarifFixAdapter( return fileReplacementsList.map { fileReplacementsListForSingleFile -> val filePath = fileReplacementsListForSingleFile.filePath // sort replacements by startLine - val sortedReplacements = fileReplacementsListForSingleFile.replacements.map { replacement -> - if (replacement.deletedRegion.endLine == null) { - val deletedRegion = replacement.deletedRegion.copy( - endLine = replacement.deletedRegion.startLine - ) - replacement.copy( - deletedRegion = deletedRegion - ) - } else { - replacement - } - }.sortedWith( - compareBy( { it.deletedRegion.startLine }, {it.deletedRegion.endLine}) - ) + val sortedReplacements = sortReplacementsByStartLine(fileReplacementsListForSingleFile) // now, from overlapping fixes, take only the first one val nonOverlappingFixes = getNonOverlappingReplacements(fileReplacementsListForSingleFile.filePath, sortedReplacements) - // save replacements in reverse order FileReplacements( filePath, @@ -179,6 +165,31 @@ class SarifFixAdapter( } } + /** + * Sort provided list of replacements by [startLine] + * + * @param fileReplacementsListForSingleFile list of replacements for target file + * @return sorted list of replacements by [startLine] + */ + private fun sortReplacementsByStartLine(fileReplacementsListForSingleFile: FileReplacements): List { + return fileReplacementsListForSingleFile.replacements.map { replacement -> + // it's not require to present endLine, if fix represents the single line changes, + // however, for consistency we will set it anyway + if (replacement.deletedRegion.endLine == null) { + val deletedRegion = replacement.deletedRegion.copy( + endLine = replacement.deletedRegion.startLine + ) + replacement.copy( + deletedRegion = deletedRegion + ) + } else { + replacement + } + }.sortedWith( + compareBy( { it.deletedRegion.startLine }, {it.deletedRegion.endLine}) + ) + } + /** * For the [sortedReplacements] list take only non overlapping replacements. * Replacement overlaps, if they are fix the same region, e.g: max(fix1.startLine, fix2.startLine) <= min(fix1.endLine, fix2.endLine), From d4b794983724bcfcb37b5f77ba3bbdc5b7965b63 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:59:57 +0300 Subject: [PATCH 08/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 2876dea..150652b 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -48,7 +48,10 @@ class SarifFixAdapter( emptyList() } else { val groupedReplacements = groupReplacementsByFiles(runReplacements) - applyReplacementsToFiles(groupedReplacements, targetFiles) + // if there are several fixes by different rules for the same region within one file, take only first of them + // and reverse the order of replacements for each file + val filteredRuleReplacements = filterRuleReplacements(groupedReplacements) + applyReplacementsToFiles(filteredRuleReplacements, targetFiles) } } return processedFiles.toList() @@ -112,35 +115,6 @@ class SarifFixAdapter( } } - - /** - * Apply fixes from single run to the target files - * - * @param fileReplacementsList list of replacements from all rules - * @param targetFiles list of target files - */ - private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { - // if there are several fixes by different rules for the same region within one file, take only first of them - // and reverse the order of replacements for each file - val filteredRuleReplacements = filterRuleReplacements(fileReplacementsList) - return filteredRuleReplacements.mapNotNull { fileReplacements -> - val targetFile = targetFiles.find { - val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { - fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) - } else { - fileReplacements.filePath - } - fs.canonicalize(it) == fullPathOfFileFromSarif - } - if (targetFile == null) { - println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") - null - } else { - applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) - } - } - } - /** * If there are several fixes in one file for the same region by different rules, take only the first one, * also return filtered replacements in reverse order, in aim to provide fixes on the next stages without invalidation of indexes, @@ -216,6 +190,31 @@ class SarifFixAdapter( return nonOverlappingFixes } + /** + * Apply fixes from single run to the target files + * + * @param fileReplacementsList list of replacements from all rules + * @param targetFiles list of target files + */ + private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { + return fileReplacementsList.mapNotNull { fileReplacements -> + val targetFile = targetFiles.find { + val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { + fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) + } else { + fileReplacements.filePath + } + fs.canonicalize(it) == fullPathOfFileFromSarif + } + if (targetFile == null) { + println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") + null + } else { + applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) + } + } + } + /** * Create copy of the target file and apply fixes from sarif * From 7777f9060ac3ed2e4762933561ee7aafc2f54391 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 17:04:52 +0300 Subject: [PATCH 09/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 97 +++++++++---------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 150652b..89c9adc 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -124,19 +124,17 @@ class SarifFixAdapter( * @param fileReplacementsList list of replacements by all rules * @return filtered list of replacements by all rules */ - private fun filterRuleReplacements(fileReplacementsList: List): List { - return fileReplacementsList.map { fileReplacementsListForSingleFile -> - val filePath = fileReplacementsListForSingleFile.filePath - // sort replacements by startLine - val sortedReplacements = sortReplacementsByStartLine(fileReplacementsListForSingleFile) - // now, from overlapping fixes, take only the first one - val nonOverlappingFixes = getNonOverlappingReplacements(fileReplacementsListForSingleFile.filePath, sortedReplacements) - // save replacements in reverse order - FileReplacements( - filePath, - nonOverlappingFixes.reversed() - ) - } + private fun filterRuleReplacements(fileReplacementsList: List): List = fileReplacementsList.map { fileReplacementsListForSingleFile -> + val filePath = fileReplacementsListForSingleFile.filePath + // sort replacements by startLine + val sortedReplacements = sortReplacementsByStartLine(fileReplacementsListForSingleFile) + // now, from overlapping fixes, take only the first one + val nonOverlappingFixes = getNonOverlappingReplacements(fileReplacementsListForSingleFile.filePath, sortedReplacements) + // save replacements in reverse order + FileReplacements( + filePath, + nonOverlappingFixes.reversed() + ) } /** @@ -145,24 +143,24 @@ class SarifFixAdapter( * @param fileReplacementsListForSingleFile list of replacements for target file * @return sorted list of replacements by [startLine] */ - private fun sortReplacementsByStartLine(fileReplacementsListForSingleFile: FileReplacements): List { - return fileReplacementsListForSingleFile.replacements.map { replacement -> - // it's not require to present endLine, if fix represents the single line changes, - // however, for consistency we will set it anyway - if (replacement.deletedRegion.endLine == null) { - val deletedRegion = replacement.deletedRegion.copy( - endLine = replacement.deletedRegion.startLine - ) - replacement.copy( - deletedRegion = deletedRegion - ) - } else { - replacement - } - }.sortedWith( - compareBy( { it.deletedRegion.startLine }, {it.deletedRegion.endLine}) - ) - } + private fun sortReplacementsByStartLine( + fileReplacementsListForSingleFile: FileReplacements + ): List = fileReplacementsListForSingleFile.replacements.map { replacement -> + // it's not require to present endLine, if fix represents the single line changes, + // however, for consistency we will set it anyway + if (replacement.deletedRegion.endLine == null) { + val deletedRegion = replacement.deletedRegion.copy( + endLine = replacement.deletedRegion.startLine + ) + replacement.copy( + deletedRegion = deletedRegion + ) + } else { + replacement + } + }.sortedWith( + compareBy({ it.deletedRegion.startLine }, { it.deletedRegion.endLine }) + ) /** * For the [sortedReplacements] list take only non overlapping replacements. @@ -181,8 +179,7 @@ class SarifFixAdapter( if (sortedReplacements[i].deletedRegion.startLine!! <= currentEndLine) { println("Fix ${sortedReplacements[i].prettyString()} for $filePath was ignored, due it overlaps with others." + " Only the first fix for this region will be applied.") - } - else { + } else { nonOverlappingFixes.add(sortedReplacements[i]) currentEndLine = sortedReplacements[i].deletedRegion.endLine!! } @@ -196,22 +193,20 @@ class SarifFixAdapter( * @param fileReplacementsList list of replacements from all rules * @param targetFiles list of target files */ - private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List { - return fileReplacementsList.mapNotNull { fileReplacements -> - val targetFile = targetFiles.find { - val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { - fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) - } else { - fileReplacements.filePath - } - fs.canonicalize(it) == fullPathOfFileFromSarif - } - if (targetFile == null) { - println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") - null + private fun applyReplacementsToFiles(fileReplacementsList: List, targetFiles: List): List = fileReplacementsList.mapNotNull { fileReplacements -> + val targetFile = targetFiles.find { + val fullPathOfFileFromSarif = if (!fileReplacements.filePath.adaptedIsAbsolute()) { + fs.canonicalize(sarifFile.parent!! / fileReplacements.filePath) } else { - applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) + fileReplacements.filePath } + fs.canonicalize(it) == fullPathOfFileFromSarif + } + if (targetFile == null) { + println("Couldn't find appropriate target file on the path ${fileReplacements.filePath}, which provided in Sarif!") + null + } else { + applyReplacementsToSingleFile(targetFile, fileReplacements.replacements) } } @@ -281,10 +276,6 @@ class SarifFixAdapter( } } - - private fun Replacement.prettyString(): String { - return "(startLine: ${this.deletedRegion.startLine}, endLine: ${this.deletedRegion.endLine}, " + - "startColumn: ${this.deletedRegion.startColumn}, endColumn: ${this.deletedRegion.endColumn}, insertedContent: ${this.insertedContent})" - - } + private fun Replacement.prettyString(): String = "(startLine: ${this.deletedRegion.startLine}, endLine: ${this.deletedRegion.endLine}, " + + "startColumn: ${this.deletedRegion.startColumn}, endColumn: ${this.deletedRegion.endColumn}, insertedContent: ${this.insertedContent})" } From 5d73c54c7074e134c96b21eb6ca271b56bf180a9 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 13:28:33 +0300 Subject: [PATCH 10/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 76 +++++++++++++++++-- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 89c9adc..0afcc6b 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -146,11 +146,29 @@ class SarifFixAdapter( private fun sortReplacementsByStartLine( fileReplacementsListForSingleFile: FileReplacements ): List = fileReplacementsListForSingleFile.replacements.map { replacement -> - // it's not require to present endLine, if fix represents the single line changes, - // however, for consistency we will set it anyway - if (replacement.deletedRegion.endLine == null) { + val updatedReplacement = recoverEndLine(replacement) + updatedReplacement + }.sortedWith( + compareBy({ it.deletedRegion.startLine }, { it.deletedRegion.endLine }) + ) + + /** + * It's not require to present endLine, if fix represents the single line changes, + * also, maybe it won't present in multiline fix too, since `insertedContent` will contain '\n' by itself + * so, for consistency we will set `endLine` by ourselves, if it absent + * + * @param replacement replacement instance, with probably missing [endLine] field + * @return updated replacement, with filled [endLine], if it was absent + */ + private fun recoverEndLine(replacement: Replacement): Replacement { + return if (replacement.deletedRegion.endLine == null) { + // count the number of lines in inserted text + val linesNumber = replacement.insertedContent?.text?.countLines() ?: 1 + // calculate shift from startLine, if linesNumber = 1 => endLine equals startLine, and shift is 0 + // else shift = linesNumber - 1 and endLine = startLine + shift + val shiftFromStartLine = linesNumber - 1 val deletedRegion = replacement.deletedRegion.copy( - endLine = replacement.deletedRegion.startLine + endLine = replacement.deletedRegion.startLine!! + shiftFromStartLine ) replacement.copy( deletedRegion = deletedRegion @@ -158,9 +176,7 @@ class SarifFixAdapter( } else { replacement } - }.sortedWith( - compareBy({ it.deletedRegion.startLine }, { it.deletedRegion.endLine }) - ) + } /** * For the [sortedReplacements] list take only non overlapping replacements. @@ -244,6 +260,52 @@ class SarifFixAdapter( return targetFileCopy } + private fun foo( + fileContent: MutableList, + insertedContent: String?, + startLine: Int, + endLine: Int, + startColumn: Int?, + endColumn: Int? + ) { + if (startLine != endLine || insertedContent.isMultiline()) { + // multiline fix + insertedContent?.let { + if (startColumn != null && endColumn != null) { + fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) + } else { + fileContent[startLine] = it + } + } ?: run { + + } + } else { + // single line fix + insertedContent?.let { + if (startColumn != null && endColumn != null) { + fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) + } else { + fileContent[startLine] = it + } + } ?: run { + + } + } + + + } + + + private fun String?.isMultiline(): Boolean { + return this?.let { content -> + content.countLines() > 1 + } == true + } + + private fun String.countLines(): Int { + return this.split('\n').filterNot { it.isBlank() }.size + } + /** * Apply single line fixes into [fileContent] * From c953d76e8d7ec47feb51c8fa5e47c558e42dff68 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 14:43:34 +0300 Subject: [PATCH 11/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 0afcc6b..2b953a7 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -268,13 +268,24 @@ class SarifFixAdapter( startColumn: Int?, endColumn: Int? ) { - if (startLine != endLine || insertedContent.isMultiline()) { + if (startLine != endLine) { // multiline fix insertedContent?.let { if (startColumn != null && endColumn != null) { - fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) + // remove characters in startLine after startColumn + fileContent[startLine] = fileContent[startLine].removeRange(startColumn, fileContent[startLine].length) + for (line in startLine + 1 until endLine) { + fileContent.removeAt(line) + } + fileContent[endLine].removeRange(0 , endColumn) + fileContent[startLine] = StringBuilder(fileContent[startLine]).apply { insert(startColumn, it) }.toString() } else { - fileContent[startLine] = it + // whole range (startLine, endLine) is changed + for (line in startLine.. endLine) { + fileContent.removeAt(line) + } + // insertedContent already contains newlines, so could be inserted simply starting from startLine + fileContent.add(startLine, it) } } ?: run { @@ -283,25 +294,26 @@ class SarifFixAdapter( // single line fix insertedContent?.let { if (startColumn != null && endColumn != null) { + // replace range fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) } else { + // replace whole line fileContent[startLine] = it } } ?: run { - + if (startColumn != null && endColumn != null) { + // remove range + fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) + } else { + // remove whole line + fileContent.removeAt(startLine) + } } } } - - private fun String?.isMultiline(): Boolean { - return this?.let { content -> - content.countLines() > 1 - } == true - } - private fun String.countLines(): Int { return this.split('\n').filterNot { it.isBlank() }.size } From 8b2b7018a56142e6ee52532b59e1a0fa2f623d16 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:15:11 +0300 Subject: [PATCH 12/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 127 ++++++++++-------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 2b953a7..14c739e 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -242,6 +242,7 @@ class SarifFixAdapter( replacements.forEach { replacement -> val startLine = replacement.deletedRegion.startLine!!.toInt() - 1 + val endLine = replacement.deletedRegion.endLine!!.toInt() - 1 val startColumn = replacement.deletedRegion.startColumn?.let { it.toInt() - 1 } @@ -250,7 +251,7 @@ class SarifFixAdapter( } val insertedContent = replacement.insertedContent?.text - applyFixToLine(fileContent, insertedContent, startLine, startColumn, endColumn) + applyFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) } fs.write(targetFileCopy) { fileContent.forEach { line -> @@ -260,7 +261,16 @@ class SarifFixAdapter( return targetFileCopy } - private fun foo( + /** + * Apply fixes into [fileContent] + * + * @param fileContent file data, where need to change content + * @param insertedContent represents inserted content into the line from [fileContent] with index [startLine], or null if fix represent the deletion of region + * @param startLine index of line, which need to be changed + * @param startColumn index of column, starting from which content should be changed, or null if [startLine] will be completely replaced + * @param endColumn index of column, ending with which content should be changed, or null if [startLine] will be completely replaced + */ + private fun applyFix( fileContent: MutableList, insertedContent: String?, startLine: Int, @@ -270,64 +280,70 @@ class SarifFixAdapter( ) { if (startLine != endLine) { // multiline fix - insertedContent?.let { - if (startColumn != null && endColumn != null) { - // remove characters in startLine after startColumn - fileContent[startLine] = fileContent[startLine].removeRange(startColumn, fileContent[startLine].length) - for (line in startLine + 1 until endLine) { - fileContent.removeAt(line) - } - fileContent[endLine].removeRange(0 , endColumn) - fileContent[startLine] = StringBuilder(fileContent[startLine]).apply { insert(startColumn, it) }.toString() - } else { - // whole range (startLine, endLine) is changed - for (line in startLine.. endLine) { - fileContent.removeAt(line) - } - // insertedContent already contains newlines, so could be inserted simply starting from startLine - fileContent.add(startLine, it) - } - } ?: run { - - } + applyMultiLineFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) } else { // single line fix - insertedContent?.let { - if (startColumn != null && endColumn != null) { - // replace range - fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) - } else { - // replace whole line - fileContent[startLine] = it - } - } ?: run { - if (startColumn != null && endColumn != null) { - // remove range - fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) - } else { - // remove whole line - fileContent.removeAt(startLine) - } - } + applySingleLineFix(fileContent, insertedContent, startLine, startColumn, endColumn) } } - private fun String.countLines(): Int { - return this.split('\n').filterNot { it.isBlank() }.size + private fun applyMultiLineFix( + fileContent: MutableList, + insertedContent: String?, + startLine: Int, + endLine: Int, + startColumn: Int?, + endColumn: Int? + ) { + insertedContent?.let { + if (startColumn != null && endColumn != null) { + removeMultiLineRange(fileContent, startLine, endLine, startColumn, endColumn) + fileContent[startLine] = StringBuilder(fileContent[startLine]).apply { insert(startColumn, it) }.toString() + } else { + removeMultiLines(fileContent, startLine, endLine) + // insertedContent already contains newlines, so could be inserted simply starting from startLine + fileContent.add(startLine, it) + } + } ?: run { + if (startColumn != null && endColumn != null) { + removeMultiLineRange(fileContent, startLine, endLine, startColumn, endColumn) + } else { + removeMultiLines(fileContent, startLine, endLine) + } + } } - /** - * Apply single line fixes into [fileContent] - * - * @param fileContent file data, where need to change content - * @param insertedContent represents inserted content into the line from [fileContent] with index [startLine], or null if fix represent the deletion of region - * @param startLine index of line, which need to be changed - * @param startColumn index of column, starting from which content should be changed, or null if [startLine] will be completely replaced - * @param endColumn index of column, ending with which content should be changed, or null if [startLine] will be completely replaced - */ - private fun applyFixToLine( + private fun removeMultiLineRange( + fileContent: MutableList, + startLine: Int, + endLine: Int, + startColumn: Int, + endColumn: Int + ) { + // remove characters in startLine after startColumn + fileContent[startLine] = fileContent[startLine].removeRange(startColumn, fileContent[startLine].length) + // remove lines between startLine and endLine + for (line in startLine + 1 until endLine) { + fileContent.removeAt(line) + } + // remove characters in endLine before endColumn + fileContent[endLine].removeRange(0 , endColumn) + } + + private fun removeMultiLines( + fileContent: MutableList, + startLine: Int, + endLine: Int, + ) { + // whole range (startLine, endLine) is changed + for (line in startLine.. endLine) { + fileContent.removeAt(line) + } + } + + private fun applySingleLineFix( fileContent: MutableList, insertedContent: String?, startLine: Int, @@ -336,20 +352,25 @@ class SarifFixAdapter( ) { insertedContent?.let { if (startColumn != null && endColumn != null) { + // replace range fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) } else { + // replace whole line fileContent[startLine] = it } } ?: run { if (startColumn != null && endColumn != null) { + // remove range fileContent[startLine] = fileContent[startLine].removeRange(startColumn, endColumn) } else { - // remove all content but leave empty line, until we support https://github.com/saveourtool/sarif-utils/issues/13 - fileContent[startLine] = "\n" + // remove whole line + fileContent.removeAt(startLine) } } } + private fun String.countLines(): Int = this.split('\n').filterNot { it.isBlank() }.size + private fun Replacement.prettyString(): String = "(startLine: ${this.deletedRegion.startLine}, endLine: ${this.deletedRegion.endLine}, " + "startColumn: ${this.deletedRegion.startColumn}, endColumn: ${this.deletedRegion.endColumn}, insertedContent: ${this.insertedContent})" } From 1c8b08f6dc2a13a7816df393309967cdbca5e50a Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:27:50 +0300 Subject: [PATCH 13/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 14c739e..9502f5b 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -160,22 +160,20 @@ class SarifFixAdapter( * @param replacement replacement instance, with probably missing [endLine] field * @return updated replacement, with filled [endLine], if it was absent */ - private fun recoverEndLine(replacement: Replacement): Replacement { - return if (replacement.deletedRegion.endLine == null) { - // count the number of lines in inserted text - val linesNumber = replacement.insertedContent?.text?.countLines() ?: 1 - // calculate shift from startLine, if linesNumber = 1 => endLine equals startLine, and shift is 0 - // else shift = linesNumber - 1 and endLine = startLine + shift - val shiftFromStartLine = linesNumber - 1 - val deletedRegion = replacement.deletedRegion.copy( - endLine = replacement.deletedRegion.startLine!! + shiftFromStartLine - ) - replacement.copy( - deletedRegion = deletedRegion - ) - } else { - replacement - } + private fun recoverEndLine(replacement: Replacement): Replacement = if (replacement.deletedRegion.endLine == null) { + // count the number of lines in inserted text + val linesNumber = replacement.insertedContent?.text?.countLines() ?: 1 + // calculate shift from startLine, if linesNumber = 1 => endLine equals startLine, and shift is 0 + // else shift = linesNumber - 1 and endLine = startLine + shift + val shiftFromStartLine = linesNumber - 1 + val deletedRegion = replacement.deletedRegion.copy( + endLine = replacement.deletedRegion.startLine!! + shiftFromStartLine + ) + replacement.copy( + deletedRegion = deletedRegion + ) + } else { + replacement } /** @@ -265,11 +263,14 @@ class SarifFixAdapter( * Apply fixes into [fileContent] * * @param fileContent file data, where need to change content - * @param insertedContent represents inserted content into the line from [fileContent] with index [startLine], or null if fix represent the deletion of region - * @param startLine index of line, which need to be changed - * @param startColumn index of column, starting from which content should be changed, or null if [startLine] will be completely replaced - * @param endColumn index of column, ending with which content should be changed, or null if [startLine] will be completely replaced + * @param insertedContent represents inserted content for the [fileContent] starting with line [startLine] and ending with [endLine], + * or null if fix represent the deletion of region + * @param startLine start index of line, which need to be changed + * @param endLine end index of line, which need to be changed + * @param startColumn index of column, starting from which content should be changed, or null if range ([startLine], [endLine]) will be completely replaced + * @param endColumn index of column, ending with which content should be changed, or null if range ([startLine], [endLine]) will be completely replaced */ + @Suppress("TOO_MANY_PARAMETERS") private fun applyFix( fileContent: MutableList, insertedContent: String?, @@ -285,10 +286,9 @@ class SarifFixAdapter( // single line fix applySingleLineFix(fileContent, insertedContent, startLine, startColumn, endColumn) } - - } + @Suppress("TOO_MANY_PARAMETERS") private fun applyMultiLineFix( fileContent: MutableList, insertedContent: String?, @@ -297,16 +297,20 @@ class SarifFixAdapter( startColumn: Int?, endColumn: Int? ) { - insertedContent?.let { + insertedContent?.let { content -> if (startColumn != null && endColumn != null) { + // first, remove changeable region removeMultiLineRange(fileContent, startLine, endLine, startColumn, endColumn) - fileContent[startLine] = StringBuilder(fileContent[startLine]).apply { insert(startColumn, it) }.toString() + // insertedContent already contains newlines, so could be inserted simply starting from startLine + fileContent[startLine] = StringBuilder(fileContent[startLine]).apply { insert(startColumn, content) }.toString() } else { + // remove whole changeable region removeMultiLines(fileContent, startLine, endLine) // insertedContent already contains newlines, so could be inserted simply starting from startLine - fileContent.add(startLine, it) + fileContent.add(startLine, content) } } ?: run { + // just remove changeable region if (startColumn != null && endColumn != null) { removeMultiLineRange(fileContent, startLine, endLine, startColumn, endColumn) } else { @@ -329,7 +333,7 @@ class SarifFixAdapter( fileContent.removeAt(line) } // remove characters in endLine before endColumn - fileContent[endLine].removeRange(0 , endColumn) + fileContent[endLine].removeRange(0, endColumn) } private fun removeMultiLines( @@ -337,8 +341,8 @@ class SarifFixAdapter( startLine: Int, endLine: Int, ) { - // whole range (startLine, endLine) is changed - for (line in startLine.. endLine) { + // remove whole range (startLine, endLine) + for (line in startLine..endLine) { fileContent.removeAt(line) } } @@ -350,13 +354,13 @@ class SarifFixAdapter( startColumn: Int?, endColumn: Int? ) { - insertedContent?.let { + insertedContent?.let { content -> if (startColumn != null && endColumn != null) { // replace range - fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, it) + fileContent[startLine] = fileContent[startLine].replaceRange(startColumn, endColumn, content) } else { // replace whole line - fileContent[startLine] = it + fileContent[startLine] = content } } ?: run { if (startColumn != null && endColumn != null) { From 5aae317022d54b378234f3333989efc8c52e287a Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:51:03 +0300 Subject: [PATCH 14/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 7 +- .../sarifutils/cli/files/FileUtils.kt | 14 ++++ .../sarifutils/adapter/SarifFixAdapterTest.kt | 32 +++++++- .../resources/sarif-multiline-fixes.sarif | 75 +++++++++++++++++++ 4 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 fixpatches/src/commonTest/resources/sarif-multiline-fixes.sarif diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 9502f5b..bf7482d 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -6,6 +6,7 @@ import com.saveourtool.sarifutils.cli.files.createTempDir import com.saveourtool.sarifutils.cli.files.fs import com.saveourtool.sarifutils.cli.files.readFile import com.saveourtool.sarifutils.cli.files.readLines +import com.saveourtool.sarifutils.cli.files.writeContentWithNewLinesToFile import com.saveourtool.sarifutils.cli.utils.adaptedIsAbsolute import com.saveourtool.sarifutils.cli.utils.getUriBaseIdForArtifactLocation import com.saveourtool.sarifutils.cli.utils.resolveUriBaseId @@ -251,11 +252,7 @@ class SarifFixAdapter( applyFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) } - fs.write(targetFileCopy) { - fileContent.forEach { line -> - writeUtf8(line + '\n') - } - } + writeContentWithNewLinesToFile(targetFileCopy, fileContent) return targetFileCopy } diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt index ef7f94a..db32c7d 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt @@ -26,6 +26,20 @@ internal fun readFile(path: Path): String = fs.read(path) { this.readUtf8() } +/** + * @param targetFile file whether to write [content] + * @param content data to be written + */ +internal fun writeContentWithNewLinesToFile(targetFile: Path, content: List) = fs.write(targetFile) { + content.forEach { line -> + if (!line.contains('\n')) { + writeUtf8(line + '\n') + } else { + writeUtf8(line) + } + } + } + /** * Create a temporary directory * diff --git a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt index 42ad5c2..1e9948a 100644 --- a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt +++ b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt @@ -298,7 +298,7 @@ class SarifFixAdapterTest { } @Test - fun `sarif fix adapter test`() { + fun `sarif fix test`() { val sarifFilePath = "src/commonTest/resources/sarif-fixes.sarif".toPath() val testFile = "src/commonTest/resources/src/kotlin/EnumValueSnakeCaseTest.kt".toPath() @@ -322,7 +322,7 @@ class SarifFixAdapterTest { } @Test - fun `sarif fix adapter test 2`() { + fun `sarif fix test 2`() { val sarifFilePath = "src/commonTest/resources/sarif-fixes-2.sarif".toPath() val testFile = "src/commonTest/resources/targets/autofix/autofix.py".toPath() @@ -350,7 +350,7 @@ class SarifFixAdapterTest { } @Test - fun `sarif fix adapter test 3`() { + fun `sarif fix test 3`() { val testFiles = listOf( "src/commonTest/resources/src/kotlin/Test1.kt".toPath(), "src/commonTest/resources/src/kotlin/Test2.kt".toPath() @@ -389,7 +389,7 @@ class SarifFixAdapterTest { } @Test - fun `sarif fix adapter test 4`() { + fun `sarif fix test 4`() { val sarifFilePath = "src/commonTest/resources/sarif-warn-and-fixes.sarif".toPath() val testFile = "src/commonTest/resources/needsfix/NeedsFix.cs".toPath() @@ -412,6 +412,30 @@ class SarifFixAdapterTest { assertEquals(expectedDelta, diff.trimIndent()) } + @Test + fun `sarif multiline fix`() { + val sarifFilePath = "src/commonTest/resources/sarif-multiline-fixes.sarif".toPath() + val testFile = "src/commonTest/resources/src/kotlin/EnumValueSnakeCaseTest.kt".toPath() + + val sarifFixAdapter = SarifFixAdapter( + sarifFile = sarifFilePath, + targetFiles = listOf(testFile) + ) + + val processedFile = sarifFixAdapter.process().first() + + val diff = calculateDiff(testFile, processedFile) + + val expectedDelta = + """ + ChangeDelta, position 8, lines: + - [NA]me[_]M[Y]a[_s]ayR[_] + + meMaayR + """.trimIndent() + + assertEquals(expectedDelta, diff.trimIndent()) + } + @Suppress("TOO_MANY_PARAMETERS") private fun compareDeletedRegion( changes: Replacement, diff --git a/fixpatches/src/commonTest/resources/sarif-multiline-fixes.sarif b/fixpatches/src/commonTest/resources/sarif-multiline-fixes.sarif new file mode 100644 index 0000000..2b9b140 --- /dev/null +++ b/fixpatches/src/commonTest/resources/sarif-multiline-fixes.sarif @@ -0,0 +1,75 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "originalUriBaseIds": { + "%SRCROOT%": { + "uri": "." + } + }, + "results": [ + { + "fixes": [ + { + "artifactChanges": [ + { + "artifactLocation": { + "uri": "src/kotlin/EnumValueSnakeCaseTest.kt" + }, + "replacements": [ + { + "deletedRegion": { + "startLine": 9 + }, + "insertedContent": { + "text": " nameMyaSayR\n" + } + } + ] + } + ], + "description": { + "text": "[ENUM_VALUE] enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_" + } + } + ], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/kotlin/EnumValueSnakeCaseTest.kt", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "snippet": { + "text": " NAme_MYa_sayR_\n" + }, + "startLine": 9 + } + } + } + ], + "message": { + "text": "[ENUM_VALUE] enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_" + }, + "ruleId": "diktat-ruleset:identifier-naming" + } + ], + "tool": { + "driver": { + "downloadUri": "https://github.com/pinterest/ktlint/releases/tag/0.42.0", + "fullName": "ktlint", + "informationUri": "https://github.com/pinterest/ktlint/", + "language": "en", + "name": "ktlint", + "organization": "pinterest", + "rules": [ + ], + "semanticVersion": "0.42.0", + "version": "0.42.0" + } + } + } + ] +} From 79a030239054c8431b808b04cdf6a724d5cf9d65 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 16:30:10 +0300 Subject: [PATCH 15/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 13 ++-- .../sarifutils/adapter/SarifFixAdapterTest.kt | 26 +++++++ .../resources/sarif-multiline-fixes-2.sarif | 75 +++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index bf7482d..39b86f1 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -253,6 +253,11 @@ class SarifFixAdapter( applyFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) } writeContentWithNewLinesToFile(targetFileCopy, fileContent) + val a = readLines(targetFileCopy).toMutableList() + println("=================\n") + a.forEach { + println(it) + } return targetFileCopy } @@ -326,9 +331,7 @@ class SarifFixAdapter( // remove characters in startLine after startColumn fileContent[startLine] = fileContent[startLine].removeRange(startColumn, fileContent[startLine].length) // remove lines between startLine and endLine - for (line in startLine + 1 until endLine) { - fileContent.removeAt(line) - } + fileContent.subList(startLine + 1, endLine - 1).clear() // remove characters in endLine before endColumn fileContent[endLine].removeRange(0, endColumn) } @@ -339,9 +342,7 @@ class SarifFixAdapter( endLine: Int, ) { // remove whole range (startLine, endLine) - for (line in startLine..endLine) { - fileContent.removeAt(line) - } + fileContent.subList(startLine, endLine).clear() } private fun applySingleLineFix( diff --git a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt index 1e9948a..6f810a9 100644 --- a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt +++ b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt @@ -436,6 +436,32 @@ class SarifFixAdapterTest { assertEquals(expectedDelta, diff.trimIndent()) } + @Test + fun `sarif multiline fix 2`() { + val sarifFilePath = "src/commonTest/resources/sarif-multiline-fixes-2.sarif".toPath() + val testFile = "src/commonTest/resources/src/kotlin/EnumValueSnakeCaseTest.kt".toPath() + + val sarifFixAdapter = SarifFixAdapter( + sarifFile = sarifFilePath, + targetFiles = listOf(testFile) + ) + + val processedFile = sarifFixAdapter.process().first() + + val diff = calculateDiff(testFile, processedFile) + + val expectedDelta = + """ + ChangeDelta, position 8, lines: + - [NA]me[_]M[Y]a[_s]ayR[_] + + meMaayR + + InsertDelta(source=[position: 10, size: 0, lines: []], target=[position: 10, size: 1, lines: [// comment]]) + """.trimIndent() + + assertEquals(expectedDelta, diff.trimIndent()) + } + @Suppress("TOO_MANY_PARAMETERS") private fun compareDeletedRegion( changes: Replacement, diff --git a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif new file mode 100644 index 0000000..9ad862a --- /dev/null +++ b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif @@ -0,0 +1,75 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "originalUriBaseIds": { + "%SRCROOT%": { + "uri": "." + } + }, + "results": [ + { + "fixes": [ + { + "artifactChanges": [ + { + "artifactLocation": { + "uri": "src/kotlin/EnumValueSnakeCaseTest.kt" + }, + "replacements": [ + { + "deletedRegion": { + "startLine": 9 + }, + "insertedContent": { + "text": " nameMyaSayR\n}\n// comment\n" + } + } + ] + } + ], + "description": { + "text": "[ENUM_VALUE] enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_" + } + } + ], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/kotlin/EnumValueSnakeCaseTest.kt", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "snippet": { + "text": " NAme_MYa_sayR_\n}\n" + }, + "startLine": 9 + } + } + } + ], + "message": { + "text": "[ENUM_VALUE] enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_" + }, + "ruleId": "diktat-ruleset:identifier-naming" + } + ], + "tool": { + "driver": { + "downloadUri": "https://github.com/pinterest/ktlint/releases/tag/0.42.0", + "fullName": "ktlint", + "informationUri": "https://github.com/pinterest/ktlint/", + "language": "en", + "name": "ktlint", + "organization": "pinterest", + "rules": [ + ], + "semanticVersion": "0.42.0", + "version": "0.42.0" + } + } + } + ] +} From 1c8164c7a4d6b9ec6a76abcb7d503a2db25f7268 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 17:04:24 +0300 Subject: [PATCH 16/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 11 ++- .../sarifutils/adapter/SarifFixAdapterTest.kt | 26 +++++++ .../resources/sarif-multiline-fixes-3.sarif | 77 +++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 39b86f1..438914b 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -19,6 +19,7 @@ import okio.Path.Companion.toPath import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import kotlin.math.min /** * Adapter for applying sarif fix object replacements to the corresponding target files @@ -281,6 +282,7 @@ class SarifFixAdapter( startColumn: Int?, endColumn: Int? ) { + println("----------------FIX startLine ${startLine} endLine ${endLine} startColumn ${startColumn} endColumn $endColumn") if (startLine != endLine) { // multiline fix applyMultiLineFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) @@ -330,10 +332,12 @@ class SarifFixAdapter( ) { // remove characters in startLine after startColumn fileContent[startLine] = fileContent[startLine].removeRange(startColumn, fileContent[startLine].length) + // TODO // remove lines between startLine and endLine - fileContent.subList(startLine + 1, endLine - 1).clear() + fileContent.subList(startLine + 1, min(fileContent.size, endLine)).clear() // remove characters in endLine before endColumn - fileContent[endLine].removeRange(0, endColumn) + // TODO + fileContent[min(fileContent.size - 1, endLine)].removeRange(0, min(endColumn, fileContent[min(fileContent.size - 1, endLine)].length)) } private fun removeMultiLines( @@ -342,7 +346,8 @@ class SarifFixAdapter( endLine: Int, ) { // remove whole range (startLine, endLine) - fileContent.subList(startLine, endLine).clear() + // TODO + fileContent.subList(startLine, min(fileContent.size, endLine + 1)).clear() } private fun applySingleLineFix( diff --git a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt index 6f810a9..da6e324 100644 --- a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt +++ b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt @@ -462,6 +462,32 @@ class SarifFixAdapterTest { assertEquals(expectedDelta, diff.trimIndent()) } + @Test + fun `sarif multiline fix 3`() { + val sarifFilePath = "src/commonTest/resources/sarif-multiline-fixes-3.sarif".toPath() + val testFile = "src/commonTest/resources/src/kotlin/EnumValueSnakeCaseTest.kt".toPath() + + val sarifFixAdapter = SarifFixAdapter( + sarifFile = sarifFilePath, + targetFiles = listOf(testFile) + ) + + val processedFile = sarifFixAdapter.process().first() + + val diff = calculateDiff(testFile, processedFile) + + val expectedDelta = + """ + ChangeDelta, position 8, lines: + - NAme[_]M[Y]a[_s]ayR[_] + + NAmeMaayR + + InsertDelta(source=[position: 10, size: 0, lines: []], target=[position: 10, size: 1, lines: [// comment]]) + """.trimIndent() + + assertEquals(expectedDelta, diff.trimIndent()) + } + @Suppress("TOO_MANY_PARAMETERS") private fun compareDeletedRegion( changes: Replacement, diff --git a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif new file mode 100644 index 0000000..5550258 --- /dev/null +++ b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif @@ -0,0 +1,77 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "originalUriBaseIds": { + "%SRCROOT%": { + "uri": "." + } + }, + "results": [ + { + "fixes": [ + { + "artifactChanges": [ + { + "artifactLocation": { + "uri": "src/kotlin/EnumValueSnakeCaseTest.kt" + }, + "replacements": [ + { + "deletedRegion": { + "endColumn": 19, + "startColumn": 9, + "startLine": 9 + }, + "insertedContent": { + "text": "MyaSayR\n}\n// comment\n" + } + } + ] + } + ], + "description": { + "text": "[ENUM_VALUE] enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_" + } + } + ], + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/kotlin/EnumValueSnakeCaseTest.kt", + "uriBaseId": "%SRCROOT%" + }, + "region": { + "snippet": { + "text": " NAme_MYa_sayR_\n}\n" + }, + "startLine": 9 + } + } + } + ], + "message": { + "text": "[ENUM_VALUE] enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_" + }, + "ruleId": "diktat-ruleset:identifier-naming" + } + ], + "tool": { + "driver": { + "downloadUri": "https://github.com/pinterest/ktlint/releases/tag/0.42.0", + "fullName": "ktlint", + "informationUri": "https://github.com/pinterest/ktlint/", + "language": "en", + "name": "ktlint", + "organization": "pinterest", + "rules": [ + ], + "semanticVersion": "0.42.0", + "version": "0.42.0" + } + } + } + ] +} From 9b6d7d163ac4af531fa95a9870d11eb84b2c802e Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 17:39:28 +0300 Subject: [PATCH 17/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 17 ++++------------- .../sarifutils/adapter/SarifFixAdapterTest.kt | 10 ++++++---- .../resources/sarif-multiline-fixes-2.sarif | 3 ++- .../resources/sarif-multiline-fixes-3.sarif | 9 +++++---- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 438914b..b9c457d 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -156,20 +156,14 @@ class SarifFixAdapter( /** * It's not require to present endLine, if fix represents the single line changes, - * also, maybe it won't present in multiline fix too, since `insertedContent` will contain '\n' by itself * so, for consistency we will set `endLine` by ourselves, if it absent * * @param replacement replacement instance, with probably missing [endLine] field * @return updated replacement, with filled [endLine], if it was absent */ private fun recoverEndLine(replacement: Replacement): Replacement = if (replacement.deletedRegion.endLine == null) { - // count the number of lines in inserted text - val linesNumber = replacement.insertedContent?.text?.countLines() ?: 1 - // calculate shift from startLine, if linesNumber = 1 => endLine equals startLine, and shift is 0 - // else shift = linesNumber - 1 and endLine = startLine + shift - val shiftFromStartLine = linesNumber - 1 val deletedRegion = replacement.deletedRegion.copy( - endLine = replacement.deletedRegion.startLine!! + shiftFromStartLine + endLine = replacement.deletedRegion.startLine ) replacement.copy( deletedRegion = deletedRegion @@ -332,12 +326,10 @@ class SarifFixAdapter( ) { // remove characters in startLine after startColumn fileContent[startLine] = fileContent[startLine].removeRange(startColumn, fileContent[startLine].length) - // TODO // remove lines between startLine and endLine - fileContent.subList(startLine + 1, min(fileContent.size, endLine)).clear() + fileContent.subList(startLine + 1, endLine).clear() // remove characters in endLine before endColumn - // TODO - fileContent[min(fileContent.size - 1, endLine)].removeRange(0, min(endColumn, fileContent[min(fileContent.size - 1, endLine)].length)) + fileContent[endLine] = fileContent[endLine].removeRange(0, endColumn) } private fun removeMultiLines( @@ -346,8 +338,7 @@ class SarifFixAdapter( endLine: Int, ) { // remove whole range (startLine, endLine) - // TODO - fileContent.subList(startLine, min(fileContent.size, endLine + 1)).clear() + fileContent.subList(startLine, endLine + 1).clear() } private fun applySingleLineFix( diff --git a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt index da6e324..13f6d30 100644 --- a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt +++ b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt @@ -478,11 +478,13 @@ class SarifFixAdapterTest { val expectedDelta = """ - ChangeDelta, position 8, lines: - - NAme[_]M[Y]a[_s]ayR[_] - + NAmeMaayR + ChangeDelta, position 7, lines: + - // ;warn:9:5: [ENUM_VALUE] [enum value]s[ sh]o[uld b]e [i]n[ s]e[lected] [UP
PER_CASE snake/Pas]c[alCase f]o[r]m[at: NA]me[_MYa_sayR_{{.*}}] + + // ;warn:9:5: [ENUM_VALUE] soe ne comme - InsertDelta(source=[position: 10, size: 0, lines: []], target=[position: 10, size: 1, lines: [// comment]]) + + - [NA]me[_]MYa_sayR_ + + meMYa_sayR_ """.trimIndent() assertEquals(expectedDelta, diff.trimIndent()) diff --git a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif index 9ad862a..ec6c7f1 100644 --- a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif +++ b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-2.sarif @@ -20,7 +20,8 @@ "replacements": [ { "deletedRegion": { - "startLine": 9 + "startLine": 9, + "endLine": 10 }, "insertedContent": { "text": " nameMyaSayR\n}\n// comment\n" diff --git a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif index 5550258..0013be6 100644 --- a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif +++ b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif @@ -20,12 +20,13 @@ "replacements": [ { "deletedRegion": { - "endColumn": 19, - "startColumn": 9, - "startLine": 9 + "endColumn": 10, + "startColumn": 32, + "startLine": 8 + "endLine": 9 }, "insertedContent": { - "text": "MyaSayR\n}\n// comment\n" + "text": "some new comment\n name" } } ] From 73f3576d9913fe2a6554a04cbf9d110905f58286 Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Wed, 21 Dec 2022 18:01:47 +0300 Subject: [PATCH 18/19] WIP ### What's done: * WIP --- .../sarifutils/cli/adapter/SarifFixAdapter.kt | 9 ----- .../sarifutils/cli/files/FileUtils.kt | 11 +++--- .../sarifutils/adapter/SarifFixAdapterTest.kt | 36 +++++++++---------- .../resources/sarif-multiline-fixes-3.sarif | 7 ++-- 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index b9c457d..684ea0a 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -19,7 +19,6 @@ import okio.Path.Companion.toPath import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.math.min /** * Adapter for applying sarif fix object replacements to the corresponding target files @@ -248,11 +247,6 @@ class SarifFixAdapter( applyFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) } writeContentWithNewLinesToFile(targetFileCopy, fileContent) - val a = readLines(targetFileCopy).toMutableList() - println("=================\n") - a.forEach { - println(it) - } return targetFileCopy } @@ -276,7 +270,6 @@ class SarifFixAdapter( startColumn: Int?, endColumn: Int? ) { - println("----------------FIX startLine ${startLine} endLine ${endLine} startColumn ${startColumn} endColumn $endColumn") if (startLine != endLine) { // multiline fix applyMultiLineFix(fileContent, insertedContent, startLine, endLine, startColumn, endColumn) @@ -367,8 +360,6 @@ class SarifFixAdapter( } } - private fun String.countLines(): Int = this.split('\n').filterNot { it.isBlank() }.size - private fun Replacement.prettyString(): String = "(startLine: ${this.deletedRegion.startLine}, endLine: ${this.deletedRegion.endLine}, " + "startColumn: ${this.deletedRegion.startColumn}, endColumn: ${this.deletedRegion.endColumn}, insertedContent: ${this.insertedContent})" } diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt index db32c7d..c6548aa 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt @@ -29,16 +29,17 @@ internal fun readFile(path: Path): String = fs.read(path) { /** * @param targetFile file whether to write [content] * @param content data to be written + * @return [Unit] */ internal fun writeContentWithNewLinesToFile(targetFile: Path, content: List) = fs.write(targetFile) { content.forEach { line -> - if (!line.contains('\n')) { - writeUtf8(line + '\n') - } else { - writeUtf8(line) - } + if (!line.contains('\n')) { + writeUtf8(line + '\n') + } else { + writeUtf8(line) } } +} /** * Create a temporary directory diff --git a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt index 13f6d30..da6876d 100644 --- a/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt +++ b/fixpatches/src/commonTest/kotlin/com/saveourtool/sarifutils/adapter/SarifFixAdapterTest.kt @@ -427,10 +427,10 @@ class SarifFixAdapterTest { val diff = calculateDiff(testFile, processedFile) val expectedDelta = - """ - ChangeDelta, position 8, lines: - - [NA]me[_]M[Y]a[_s]ayR[_] - + meMaayR + """ + ChangeDelta, position 8, lines: + - [NA]me[_]M[Y]a[_s]ayR[_] + + meMaayR """.trimIndent() assertEquals(expectedDelta, diff.trimIndent()) @@ -451,12 +451,12 @@ class SarifFixAdapterTest { val diff = calculateDiff(testFile, processedFile) val expectedDelta = - """ - ChangeDelta, position 8, lines: - - [NA]me[_]M[Y]a[_s]ayR[_] - + meMaayR - - InsertDelta(source=[position: 10, size: 0, lines: []], target=[position: 10, size: 1, lines: [// comment]]) + """ + ChangeDelta, position 8, lines: + - [NA]me[_]M[Y]a[_s]ayR[_] + + meMaayR + + InsertDelta(source=[position: 10, size: 0, lines: []], target=[position: 10, size: 1, lines: [// comment]]) """.trimIndent() assertEquals(expectedDelta, diff.trimIndent()) @@ -477,14 +477,14 @@ class SarifFixAdapterTest { val diff = calculateDiff(testFile, processedFile) val expectedDelta = - """ - ChangeDelta, position 7, lines: - - // ;warn:9:5: [ENUM_VALUE] [enum value]s[ sh]o[uld b]e [i]n[ s]e[lected] [UP
PER_CASE snake/Pas]c[alCase f]o[r]m[at: NA]me[_MYa_sayR_{{.*}}] - + // ;warn:9:5: [ENUM_VALUE] soe ne comme - - - - [NA]me[_]MYa_sayR_ - + meMYa_sayR_ + """ + ChangeDelta, position 7, lines: + - // ;warn:9:5: [ENUM_VALUE] [enum value]s[ sh]o[uld b]e [i]n[ s]e[lected] [UP
PER_CASE snake/Pas]c[alCase f]o[r]m[at: NA]me[_MYa_sayR_{{.*}}] + + // ;warn:9:5: [ENUM_VALUE] soe ne comme + + + - [NA]me[_]MYa_sayR_ + + meMYa_sayR_ """.trimIndent() assertEquals(expectedDelta, diff.trimIndent()) diff --git a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif index 0013be6..45ba04f 100644 --- a/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif +++ b/fixpatches/src/commonTest/resources/sarif-multiline-fixes-3.sarif @@ -46,9 +46,12 @@ }, "region": { "snippet": { - "text": " NAme_MYa_sayR_\n}\n" + "text": "enum values should be in selected UPPER_CASE snake/PascalCase format: NAme_MYa_sayR_{{.*}}\n NAme_" }, - "startLine": 9 + "endColumn": 10, + "startColumn": 32, + "startLine": 8 + "endLine": 9 } } } From 4f142a355f4b81a05c2ba02426373b0f32485f3d Mon Sep 17 00:00:00 2001 From: Kirill Gevorkyan <26010098+kgevorkyan@users.noreply.github.com> Date: Thu, 22 Dec 2022 16:58:55 +0300 Subject: [PATCH 19/19] Review ### What's done: * Review --- .../com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt | 2 +- .../kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt index 684ea0a..cde3fef 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/adapter/SarifFixAdapter.kt @@ -45,7 +45,7 @@ class SarifFixAdapter( val processedFiles = sarifSchema210.runs.asSequence().flatMapIndexed { index, run -> val runReplacements: List = extractFixObjects(run) if (runReplacements.isEmpty()) { - println("Run #$index have no any `fix object` section!") + println("The run #$index doesn't have any `fix object` section!") emptyList() } else { val groupedReplacements = groupReplacementsByFiles(runReplacements) diff --git a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt index c6548aa..54c6f2a 100644 --- a/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt +++ b/fixpatches/src/commonMain/kotlin/com/saveourtool/sarifutils/cli/files/FileUtils.kt @@ -27,6 +27,9 @@ internal fun readFile(path: Path): String = fs.read(path) { } /** + * Write [content] to the [targetFile], some of the elements in [content] could represent the multiline strings, + * which already contain all necessary escape characters, in this case, write them as-is, otherwise add newline at the end + * * @param targetFile file whether to write [content] * @param content data to be written * @return [Unit]