Skip to content

Commit 7edc482

Browse files
committed
Support inline fixers for fix and warn plugin
What's done: * Added support inline fixers for fix and warn plugin Closes #221
1 parent 24279e9 commit 7edc482

File tree

4 files changed

+146
-61
lines changed

4 files changed

+146
-61
lines changed

save-plugins/fix-and-warn-plugin/src/commonMain/kotlin/org/cqfn/save/plugins/fixandwarn/FixAndWarnPlugin.kt

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.cqfn.save.core.result.Pass
99
import org.cqfn.save.core.result.TestResult
1010
import org.cqfn.save.plugin.warn.WarnPlugin
1111
import org.cqfn.save.plugin.warn.WarnPluginConfig
12+
import org.cqfn.save.plugin.warn.utils.getLineNumber
1213
import org.cqfn.save.plugins.fix.FixPlugin
1314
import org.cqfn.save.plugins.fix.FixPluginConfig
1415

@@ -34,12 +35,12 @@ class FixAndWarnPlugin(
3435
fileSystem,
3536
useInternalRedirections,
3637
redirectTo) {
37-
private val fixPluginConfig: FixPluginConfig =
38-
testConfig.pluginConfigs.filterIsInstance<FixAndWarnPluginConfig>().single().fix
39-
private val warnPluginConfig: WarnPluginConfig =
40-
testConfig.pluginConfigs.filterIsInstance<FixAndWarnPluginConfig>().single().warn
38+
private val fixAndWarnPluginConfig: FixAndWarnPluginConfig = testConfig.pluginConfigs.filterIsInstance<FixAndWarnPluginConfig>().single()
39+
private val fixPluginConfig: FixPluginConfig = fixAndWarnPluginConfig.fix
40+
private val warnPluginConfig: WarnPluginConfig = fixAndWarnPluginConfig.warn
4141
private val generalConfig: GeneralConfig =
4242
testConfig.pluginConfigs.filterIsInstance<GeneralConfig>().single()
43+
private val inlineFixer = fixAndWarnPluginConfig.inlineFixer
4344
private lateinit var fixPlugin: FixPlugin
4445
private lateinit var warnPlugin: WarnPlugin
4546

@@ -68,12 +69,19 @@ class FixAndWarnPlugin(
6869
// Need to update private fields after validation
6970
initOrUpdateConfigs()
7071
val testFilePattern = warnPluginConfig.resourceNamePattern
71-
val expectedFiles = files.filterTestResources(testFilePattern, match = false)
72+
73+
val newFiles = if (inlineFixer == true) {
74+
replaceFixLine(files)
75+
} else {
76+
files
77+
}
78+
79+
val expectedFiles = newFiles.filterTestResources(testFilePattern, match = false)
7280

7381
// Remove (in place) warnings from test files before fix plugin execution
7482
val filesAndTheirWarningsMap = removeWarningsFromExpectedFiles(expectedFiles)
7583

76-
val fixTestResults = fixPlugin.handleFiles(files).toList()
84+
val fixTestResults = fixPlugin.handleFiles(newFiles).toList()
7785

7886
val (fixTestResultsPassed, fixTestResultsFailed) = fixTestResults.partition { it.status is Pass }
7987

@@ -167,6 +175,61 @@ class FixAndWarnPlugin(
167175
}
168176
}
169177

178+
@Suppress(
179+
"TOO_LONG_FUNCTION",
180+
"TOO_MANY_LINES_IN_LAMBDA"
181+
)
182+
private fun replaceFixLine(files: Sequence<List<Path>>): Sequence<List<Path>> {
183+
val newFiles: MutableList<List<Path>> = mutableListOf()
184+
files.map { file -> file.single() }.map {
185+
val fileCopy = createTestFile(it)
186+
val fileCopyWarning = createTestFile(it)
187+
val linesFile = fs.readLines(it) as MutableList
188+
linesFile.mapIndexed { index, line ->
189+
if (generalConfig.expectedWarningsPattern!!.matches(line)) {
190+
val lineNumber = line.getLineNumber(
191+
generalConfig.expectedWarningsPattern!!,
192+
warnPluginConfig.lineCaptureGroup,
193+
warnPluginConfig.linePlaceholder!!,
194+
index + 1,
195+
it,
196+
linesFile,
197+
)
198+
val newLine = fixAndWarnPluginConfig.checkFixesPattern!!.find(linesFile[index + 1])?.groups?.get(1)
199+
?.value
200+
newLine?.let {
201+
lineNumber?.let {
202+
linesFile.removeAt(lineNumber - 1)
203+
linesFile.add(lineNumber - 1, newLine)
204+
}
205+
}
206+
}
207+
}
208+
linesFile.map {
209+
if (!generalConfig.expectedWarningsPattern!!.matches(it) && !fixAndWarnPluginConfig.checkFixesPattern!!.matches(it)) {
210+
fs.write(fileCopy) {
211+
write((it + "\n").encodeToByteArray())
212+
}
213+
}
214+
}
215+
linesFile.map {
216+
if (!fixAndWarnPluginConfig.checkFixesPattern!!.matches(it)) {
217+
fs.write(fileCopyWarning) {
218+
write((it + "\n").encodeToByteArray())
219+
}
220+
}
221+
}
222+
newFiles.add(listOf(fileCopy, fileCopyWarning))
223+
}
224+
return newFiles.asSequence()
225+
}
226+
227+
private fun createTestFile(path: Path): Path {
228+
val pathCopy: Path = constructPathForCopyOfTestFile(FixAndWarnPlugin::class.simpleName!!, path)
229+
createTempDir(pathCopy.parent!!)
230+
return pathCopy
231+
}
232+
170233
override fun cleanupTempDir() {
171234
val tmpDir = (FileSystem.SYSTEM_TEMPORARY_DIRECTORY / FixAndWarnPlugin::class.simpleName!!)
172235
if (fs.exists(tmpDir)) {

save-plugins/fix-and-warn-plugin/src/commonMain/kotlin/org/cqfn/save/plugins/fixandwarn/FixAndWarnPluginConfig.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.cqfn.save.plugins.fixandwarn
22

33
import org.cqfn.save.core.config.TestConfigSections
44
import org.cqfn.save.core.plugin.PluginConfig
5+
import org.cqfn.save.core.utils.RegexSerializer
56
import org.cqfn.save.plugin.warn.WarnPluginConfig
67
import org.cqfn.save.plugins.fix.FixPluginConfig
78

@@ -10,15 +11,20 @@ import okio.Path.Companion.toPath
1011

1112
import kotlinx.serialization.Serializable
1213
import kotlinx.serialization.Transient
14+
import kotlinx.serialization.UseSerializers
1315

1416
/**
1517
* @property fix config for nested [fix] section
1618
* @property warn config for nested [warn] section
19+
* @property inlineFixer
20+
* @property checkFixesPattern
1721
*/
1822
@Serializable
1923
data class FixAndWarnPluginConfig(
2024
val fix: FixPluginConfig,
21-
val warn: WarnPluginConfig
25+
val warn: WarnPluginConfig,
26+
val inlineFixer: Boolean? = null,
27+
val checkFixesPattern: Regex? = null,
2228
) : PluginConfig {
2329
override val type = TestConfigSections.`FIX AND WARN`
2430

@@ -31,7 +37,9 @@ data class FixAndWarnPluginConfig(
3137
val mergedWarnPluginConfig = warn.mergeWith(other.warn)
3238
return FixAndWarnPluginConfig(
3339
mergedFixPluginConfig as FixPluginConfig,
34-
mergedWarnPluginConfig as WarnPluginConfig
40+
mergedWarnPluginConfig as WarnPluginConfig,
41+
this.inlineFixer ?: otherConfig.inlineFixer,
42+
this.checkFixesPattern ?: other.checkFixesPattern,
3543
)
3644
}
3745

@@ -47,8 +55,21 @@ data class FixAndWarnPluginConfig(
4755
[warn]: {${warnPluginConfig.testNameSuffix}, ${warnPluginConfig.batchSize}}
4856
"""
4957
}
58+
val newInlineFixer = inlineFixer ?: false
59+
val newCheckFixerPattern = if (newInlineFixer) (checkFixesPattern ?: defaultCheckFixesPattern) else null
5060
return FixAndWarnPluginConfig(
51-
fixPluginConfig, warnPluginConfig
61+
fixPluginConfig,
62+
warnPluginConfig,
63+
newInlineFixer,
64+
newCheckFixerPattern,
5265
)
5366
}
67+
68+
companion object {
69+
/**
70+
* Default regex for check fixes
71+
* ```CHECK-FIXES: val foo = 42```
72+
*/
73+
internal val defaultCheckFixesPattern = Regex("(.+): (.+)")
74+
}
5475
}

save-plugins/fix-and-warn-plugin/src/commonNonJsTest/kotlin/org/cqfn/save/plugins/fixandwarn/FixAndWarnPluginTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ class FixAndWarnPluginTest {
8181
WarnPluginConfig(warnExecutionCmd,
8282
Regex("(.+):(\\d+):(\\d+): (.+)"),
8383
true, true, 1, ", ", 1, 2, 3, 1, 2, 3, 4
84-
)
84+
),
85+
false,
8586
),
8687
GeneralConfig("", listOf(""), "", "", expectedWarningsPattern = Regex("// ;warn:(\\d+):(\\d+): (.*)"))
8788
),

save-plugins/warn-plugin/src/commonMain/kotlin/org/cqfn/save/plugin/warn/utils/Warning.kt

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,57 @@ data class Warning(
2525
val fileName: String,
2626
)
2727

28+
/**
29+
* @param warningRegex regular expression for warning
30+
* @param lineGroupIdx index of capture group for line number
31+
* @param placeholder placeholder for line
32+
* @param lineNum number of line
33+
* @param file path to test file
34+
* @param linesFile lines of file
35+
* @return a [Warning] or null if [this] string doesn't match [warningRegex]
36+
* @throws ResourceFormatException when parsing a file
37+
*/
38+
@Suppress(
39+
"TooGenericExceptionCaught",
40+
"LongParameterList",
41+
"NestedBlockDepth",
42+
"ReturnCount",
43+
// fixme: add `cause` parameter to `PluginException`
44+
"SwallowedException",
45+
"TOO_MANY_PARAMETERS",
46+
"AVOID_NULL_CHECKS",
47+
)
48+
fun String.getLineNumber(warningRegex: Regex,
49+
lineGroupIdx: Long?,
50+
placeholder: String,
51+
lineNum: Int?,
52+
file: Path?,
53+
linesFile: List<String>?,
54+
): Int? {
55+
if (lineGroupIdx == null) {
56+
// line capture group is not configured in save.toml
57+
return null
58+
}
59+
60+
val groups = warningRegex.find(this)?.groups ?: return null
61+
val lineValue = groups[lineGroupIdx.toInt()]!!.value
62+
return if (lineValue.isEmpty() && lineNum != null && linesFile != null) {
63+
nextLineNotMatchingRegex(file!!, warningRegex, linesFile, lineNum)
64+
} else {
65+
lineValue.toIntOrNull() ?: run {
66+
if (lineValue[0] != placeholder[0]) {
67+
throw ResourceFormatException("The group <$lineValue> is neither a number nor a placeholder.")
68+
}
69+
try {
70+
val adjustment = lineValue.substringAfterLast(placeholder)
71+
lineNum!! + adjustment.ifBlank { "0" }.toInt()
72+
} catch (e: Exception) {
73+
throw ResourceFormatException("Could not extract line number from line [$this], cause: ${e.describe()}")
74+
}
75+
}
76+
}
77+
}
78+
2879
/**
2980
* Extract warning from [this] string using provided parameters
3081
*
@@ -80,57 +131,6 @@ internal fun String.extractWarning(warningRegex: Regex,
80131
return extractWarning(warningRegex, fileName, line, columnGroupIdx, messageGroupIdx)
81132
}
82133

83-
/**
84-
* @param warningRegex regular expression for warning
85-
* @param lineGroupIdx index of capture group for line number
86-
* @param placeholder placeholder for line
87-
* @param lineNum number of line
88-
* @param file path to test file
89-
* @param linesFile lines of file
90-
* @return a [Warning] or null if [this] string doesn't match [warningRegex]
91-
* @throws ResourceFormatException when parsing a file
92-
*/
93-
@Suppress(
94-
"TooGenericExceptionCaught",
95-
"LongParameterList",
96-
"NestedBlockDepth",
97-
"ReturnCount",
98-
// fixme: add `cause` parameter to `PluginException`
99-
"SwallowedException",
100-
"TOO_MANY_PARAMETERS",
101-
"AVOID_NULL_CHECKS",
102-
)
103-
internal fun String.getLineNumber(warningRegex: Regex,
104-
lineGroupIdx: Long?,
105-
placeholder: String,
106-
lineNum: Int?,
107-
file: Path?,
108-
linesFile: List<String>?,
109-
): Int? {
110-
if (lineGroupIdx == null) {
111-
// line capture group is not configured in save.toml
112-
return null
113-
}
114-
115-
val groups = warningRegex.find(this)?.groups ?: return null
116-
val lineValue = groups[lineGroupIdx.toInt()]!!.value
117-
return if (lineValue.isEmpty() && lineNum != null && linesFile != null) {
118-
nextLineNotMatchingRegex(file!!, warningRegex, linesFile, lineNum)
119-
} else {
120-
lineValue.toIntOrNull() ?: run {
121-
if (lineValue[0] != placeholder[0]) {
122-
throw ResourceFormatException("The group <$lineValue> is neither a number nor a placeholder.")
123-
}
124-
try {
125-
val adjustment = lineValue.substringAfterLast(placeholder)
126-
lineNum!! + adjustment.ifBlank { "0" }.toInt()
127-
} catch (e: Exception) {
128-
throw ResourceFormatException("Could not extract line number from line [$this], cause: ${e.describe()}")
129-
}
130-
}
131-
}
132-
}
133-
134134
/**
135135
* @param warningRegex regular expression for warning
136136
* @param lineGroupIdx index of capture group for line number

0 commit comments

Comments
 (0)