From cf192766a62159b05816c77beef36f6fe8563c3e Mon Sep 17 00:00:00 2001 From: Andrey Shcheglov Date: Wed, 16 Nov 2022 12:13:17 +0300 Subject: [PATCH 01/33] Add `experimental`, `test`, and `custom` to the list of ignored rule set ids (#1568) ### What's done: Additional rule set ids (available since KtLint 0.47) are now ignored, both via the `--disabled_rules` command-line argument, and via class path exclusions (Gradle plug-in). This change fixes behaviour described in #1559 with KtLint 0.47+. --- .github/workflows/build_and_test.yml | 14 ++--- .github/workflows/codeql-analysis.yml | 4 +- .github/workflows/diktat.yml | 2 +- .github/workflows/diktat_snapshot.yml | 2 +- README.md | 2 +- .../plugin/gradle/DiktatGradlePlugin.kt | 24 +++++++-- .../plugin/gradle/DiktatJavaExecTaskBase.kt | 14 +++++ .../plugin/gradle/DiktatJavaExecTaskTest.kt | 53 ++++++++++++++++--- .../ruleset/rules/DiktatRuleSetProvider.kt | 9 +++- .../src/test/resources/test/smoke/save.toml | 2 +- .../test/smoke/src/main/kotlin/save.toml | 2 +- 11 files changed, 99 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 1fb0292c04..404a8c3eaa 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -77,7 +77,7 @@ jobs: - name: Run diKTat from cli continue-on-error: true run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard 'examples/maven/src/main/kotlin/Test.kt' &>out.txt + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom 'examples/maven/src/main/kotlin/Test.kt' &>out.txt shell: bash - name: Check output @@ -91,14 +91,14 @@ jobs: continue-on-error: true if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard "$PWD/examples/maven/src/main/kotlin/Test.kt" &>out.txt + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom "$PWD/examples/maven/src/main/kotlin/Test.kt" &>out.txt shell: bash - name: Run diKTat from cli on windows (absolute paths) continue-on-error: true if: runner.os == 'Windows' run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard "%cd%/examples/maven/src/main/kotlin/Test.kt" > out.txt 2>&1 + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom "%cd%/examples/maven/src/main/kotlin/Test.kt" > out.txt 2>&1 shell: cmd - name: Check output (absolute paths) @@ -111,7 +111,7 @@ jobs: - name: Run diKTat from cli (glob paths, 1 of 4) continue-on-error: true run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard 'examples/maven/src/main/kotlin/*.kt' &>out.txt + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom 'examples/maven/src/main/kotlin/*.kt' &>out.txt shell: bash - name: Check output (glob paths, 1 of 4) @@ -124,7 +124,7 @@ jobs: - name: Run diKTat from cli (glob paths, 2 of 4) continue-on-error: true run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard 'examples/**/main/kotlin/*.kt' &>out.txt + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom 'examples/**/main/kotlin/*.kt' &>out.txt shell: bash - name: Check output (glob paths, 2 of 4) @@ -137,7 +137,7 @@ jobs: - name: Run diKTat from cli (glob paths, 3 of 4) continue-on-error: true run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard 'examples/**/*.kt' &>out.txt + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom 'examples/**/*.kt' &>out.txt shell: bash - name: Check output (glob paths, 3 of 4) @@ -150,7 +150,7 @@ jobs: - name: Run diKTat from cli (glob paths, 4 of 4) continue-on-error: true run: | - java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard '**/*.kt' &>out.txt + java -jar ktlint -R diktat-${{ env.DIKTAT_VERSION }}.jar --disabled_rules=standard,experimental,test,custom '**/*.kt' &>out.txt shell: bash - name: Check output (glob paths, 4 of 4) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3601aaad0e..f943abef18 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -7,10 +7,10 @@ name: "CodeQL" on: push: - branches: [master] + branches: [ master, feature/ktlint-wrapper ] pull_request: # The branches below must be a subset of the branches above - branches: [master] + branches: [ master, feature/ktlint-wrapper ] schedule: - cron: '0 20 * * 5' diff --git a/.github/workflows/diktat.yml b/.github/workflows/diktat.yml index eab0baa8e0..dfd3a07a1c 100644 --- a/.github/workflows/diktat.yml +++ b/.github/workflows/diktat.yml @@ -4,7 +4,7 @@ on: push: branches: [ master ] pull_request: - branches: [ master ] + branches: [ master, feature/ktlint-wrapper ] jobs: diktat_check: diff --git a/.github/workflows/diktat_snapshot.yml b/.github/workflows/diktat_snapshot.yml index 196e675897..f303bf267d 100644 --- a/.github/workflows/diktat_snapshot.yml +++ b/.github/workflows/diktat_snapshot.yml @@ -4,7 +4,7 @@ on: push: branches: [ master ] pull_request: - branches: [ master ] + branches: [ master, feature/ktlint-wrapper ] env: GRADLE_OPTS: -Dorg.gradle.daemon=false diff --git a/README.md b/README.md index a942c33792..b715d9515f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Main features of diktat are the following: Finally, run KTlint (with diKTat injected) to check your '*.kt' files in 'dir/your/dir': ```console -$ ./ktlint -R diktat.jar --disabled_rules=standard "dir/your/dir/**/*.kt" +$ ./ktlint -R diktat.jar --disabled_rules=standard,experimental,test,custom "dir/your/dir/**/*.kt" ``` To **autofix** all code style violations, use `-F` option. diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt index f0a0acbc36..e444c458b6 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt @@ -17,6 +17,7 @@ class DiktatGradlePlugin : Plugin { /** * @param project a gradle [Project] that the plugin is applied to */ + @Suppress("TOO_LONG_FUNCTION") override fun apply(project: Project) { val patternSet = PatternSet() val diktatExtension = project.extensions.create( @@ -31,12 +32,25 @@ class DiktatGradlePlugin : Plugin { val diktatConfiguration = project.configurations.create(DIKTAT_CONFIGURATION) { configuration -> configuration.isVisible = false configuration.dependencies.add(project.dependencies.create("com.pinterest:ktlint:$KTLINT_VERSION", closureOf { - exclude( - mutableMapOf( - "group" to "com.pinterest.ktlint", - "module" to "ktlint-ruleset-standard" - ) + /* + * Prevent the discovery of standard rules by excluding them as + * dependencies. + */ + val ktlintRuleSets = sequenceOf( + "standard", + "experimental", + "test", + "template" ) + ktlintRuleSets.forEach { ktlintRuleSet -> + exclude( + mutableMapOf( + "group" to "com.pinterest.ktlint", + "module" to "ktlint-ruleset-$ktlintRuleSet" + ) + ) + } + attributes { it.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.EXTERNAL)) } diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt index abd9e6fccf..ee31abba17 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt @@ -92,6 +92,20 @@ open class DiktatJavaExecTaskBase @Inject constructor( project.logger.info("Setting system property for diktat config to $it") }) args = additionalFlags.toMutableList().apply { + /*- + * Disable the standard rules via the command line. + * + * Classpath exclusion (see `DiktatGradlePlugin`) is enough, but + * this is better left enabled as a safety net. + */ + run { + val ktlintRuleSetIds = sequenceOf("standard", "experimental", "test", "custom") + ktlintRuleSetIds.joinToString( + prefix = "--disabled_rules=", + separator = "," + ) + }.let(::add) + if (diktatExtension.debug) { add("--debug") } diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt index a4d79fb08d..82a9043b7f 100644 --- a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt @@ -33,7 +33,12 @@ class DiktatJavaExecTaskTest { @Test fun `check command line for various inputs`() { assertCommandLineEquals( - listOf(null, combinePathParts("src", "main", "kotlin", "Test.kt"), "--reporter=plain") + listOf( + null, + DISABLED_RULES, + combinePathParts("src", "main", "kotlin", "Test.kt"), + "--reporter=plain" + ) ) { inputs { include("src/**/*.kt") } } @@ -42,7 +47,13 @@ class DiktatJavaExecTaskTest { @Test fun `check command line in debug mode`() { assertCommandLineEquals( - listOf(null, "--debug", combinePathParts("src", "main", "kotlin", "Test.kt"), "--reporter=plain") + listOf( + null, + DISABLED_RULES, + "--debug", + combinePathParts("src", "main", "kotlin", "Test.kt"), + "--reporter=plain" + ) ) { inputs { include("src/**/*.kt") } debug = true @@ -54,7 +65,12 @@ class DiktatJavaExecTaskTest { project.file("src/main/kotlin/generated").mkdirs() project.file("src/main/kotlin/generated/Generated.kt").createNewFile() assertCommandLineEquals( - listOf(null, combinePathParts("src", "main", "kotlin", "Test.kt"), "--reporter=plain") + listOf( + null, + DISABLED_RULES, + combinePathParts("src", "main", "kotlin", "Test.kt"), + "--reporter=plain" + ) ) { inputs { include("src/**/*.kt") @@ -91,7 +107,11 @@ class DiktatJavaExecTaskTest { @Test fun `check command line has reporter type and output`() { assertCommandLineEquals( - listOf(null, "--reporter=json,output=${project.projectDir.resolve("some.txt")}") + listOf( + null, + DISABLED_RULES, + "--reporter=json,output=${project.projectDir.resolve("some.txt")}" + ) ) { inputs { exclude("*") } diktatConfigFile = project.file("../diktat-analysis.yml") @@ -103,7 +123,11 @@ class DiktatJavaExecTaskTest { @Test fun `check command line has reporter type without output`() { assertCommandLineEquals( - listOf(null, "--reporter=json") + listOf( + null, + DISABLED_RULES, + "--reporter=json" + ) ) { inputs { exclude("*") } diktatConfigFile = project.file("../diktat-analysis.yml") @@ -115,7 +139,11 @@ class DiktatJavaExecTaskTest { fun `check command line in githubActions mode`() { val path = project.file("${project.buildDir}/reports/diktat/diktat.sarif") assertCommandLineEquals( - listOf(null, "--reporter=sarif,output=$path") + listOf( + null, + DISABLED_RULES, + "--reporter=sarif,output=$path" + ) ) { inputs { exclude("*") } diktatConfigFile = project.file("../diktat-analysis.yml") @@ -132,7 +160,11 @@ class DiktatJavaExecTaskTest { fun `githubActions mode should have higher precedence over explicit reporter`() { val path = project.file("${project.buildDir}/reports/diktat/diktat.sarif") assertCommandLineEquals( - listOf(null, "--reporter=sarif,output=$path") + listOf( + null, + DISABLED_RULES, + "--reporter=sarif,output=$path" + ) ) { inputs { exclude("*") } diktatConfigFile = project.file("../diktat-analysis.yml") @@ -145,7 +177,11 @@ class DiktatJavaExecTaskTest { @Test fun `should set system property with SARIF reporter`() { assertCommandLineEquals( - listOf(null, "--reporter=sarif") + listOf( + null, + DISABLED_RULES, + "--reporter=sarif" + ) ) { inputs { exclude("*") } diktatConfigFile = project.file("../diktat-analysis.yml") @@ -232,5 +268,6 @@ class DiktatJavaExecTaskTest { companion object { private const val DIKTAT_CHECK_TASK = "diktatCheck" + private const val DISABLED_RULES = "--disabled_rules=standard,experimental,test,custom" } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt index 661056c931..2dbe192b50 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt @@ -98,11 +98,16 @@ import java.io.File /** * [RuleSetProvider] that provides diKTat ruleset. * By default, it is expected to have diktat-analysis.yml configuration in the root folder where 'ktlint' is run - * otherwise it will use default configuration where some rules are disabled + * otherwise it will use default configuration where some rules are disabled. + * + * The no-argument constructor is used by the Java SPI interface; that's why + * it's explicitly annotated with [JvmOverloads]. * * @param diktatConfigFile - configuration file where all configurations for inspections and rules are stored */ -class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleSetProvider { +class DiktatRuleSetProvider +@JvmOverloads +constructor(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleSetProvider { private val possibleConfigs: Sequence = sequence { yield(resolveDefaultConfig()) yield(resolveConfigFileFromJarLocation()) diff --git a/diktat-ruleset/src/test/resources/test/smoke/save.toml b/diktat-ruleset/src/test/resources/test/smoke/save.toml index bebb752e9d..ba0e3acee4 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/save.toml @@ -9,7 +9,7 @@ timeOutMillis = 3600000 ["fix and warn"] ["fix and warn".fix] - execFlags="--disabled_rules=standard -F" + execFlags="--disabled_rules=standard,experimental,test,custom -F" ["fix and warn".warn] lineCaptureGroup = 1 columnCaptureGroup = 2 diff --git a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml index 29c5a68b62..f4d8eb501e 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml @@ -8,7 +8,7 @@ expectedWarningsPattern = "// ;warn:?(.*):(\\d*): (.+)" ["fix and warn"] ["fix and warn".fix] - execFlags="--disabled_rules=standard -F" + execFlags="--disabled_rules=standard,experimental,test,custom -F" ["fix and warn".warn] actualWarningsPattern = "(\\w+\\..+):(\\d+):(\\d+): (\\[.*\\].*)$" exactWarningsMatch = false From b8f4350e13288ee2fa258608fc707f11f7c0043f Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 16 Nov 2022 13:57:02 +0300 Subject: [PATCH 02/33] Builder to run Diktat (#1562) --- diktat-maven-plugin/pom.xml | 2 +- .../diktat/plugin/maven/DiktatBaseMojo.kt | 59 ++++----- .../cqfn/diktat/plugin/maven/DiktatMojo.kt | 25 ++-- diktat-ruleset/pom.xml | 35 ++--- .../org/cqfn/diktat/DiktatProcessCommand.kt | 121 ++++++++++++++++++ .../org/cqfn/diktat/api/DiktatCallback.kt | 17 +++ .../kotlin/org/cqfn/diktat/api/DiktatError.kt | 31 +++++ .../org/cqfn/diktat/api/DiktatLogLevel.kt | 10 ++ .../diktat/ktlint/LintErrorCallbackWrapper.kt | 22 ++++ .../cqfn/diktat/ktlint/LintErrorWrapper.kt | 34 +++++ 10 files changed, 295 insertions(+), 61 deletions(-) create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt diff --git a/diktat-maven-plugin/pom.xml b/diktat-maven-plugin/pom.xml index 4437b54055..e1244be738 100644 --- a/diktat-maven-plugin/pom.xml +++ b/diktat-maven-plugin/pom.xml @@ -48,7 +48,7 @@ org.cqfn.diktat - diktat-rules + diktat ${project.version} diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 311564e9eb..878b822ea6 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -4,13 +4,13 @@ package org.cqfn.diktat.plugin.maven -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.DiktatProcessCommand +import org.cqfn.diktat.api.DiktatLogLevel +import org.cqfn.diktat.ktlint.unwrap -import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Reporter import com.pinterest.ktlint.core.RuleExecutionException -import com.pinterest.ktlint.core.RuleSet import com.pinterest.ktlint.core.api.Baseline import com.pinterest.ktlint.core.api.Baseline.Status.VALID import com.pinterest.ktlint.core.api.containsLintError @@ -98,9 +98,9 @@ abstract class DiktatBaseMojo : AbstractMojo() { private lateinit var mavenSession: MavenSession /** - * @param params instance of [KtLint.ExperimentalParams] used in analysis + * @param command instance of [DiktatProcessCommand] used in analysis */ - abstract fun runAction(params: KtLint.ExperimentalParams) + abstract fun runAction(command: DiktatProcessCommand) /** * Perform code check using diktat ruleset @@ -117,9 +117,6 @@ abstract class DiktatBaseMojo : AbstractMojo() { if (excludes.isNotEmpty()) " and excluding $excludes" else "" ) - val ruleSets by lazy { - listOf(DiktatRuleSetProvider(configFile).get()) - } val baselineResults = baseline?.let { loadBaseline(it.absolutePath) } ?: Baseline(status = VALID) reporterImpl = resolveReporter(baselineResults) @@ -129,7 +126,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { inputs .map(::File) .forEach { - checkDirectory(it, lintErrors, baselineResults.lintErrorsPerFile, ruleSets) + checkDirectory(it, lintErrors, baselineResults.lintErrorsPerFile) } reporterImpl.afterAll() @@ -201,8 +198,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { private fun checkDirectory( directory: File, lintErrors: MutableList, - baselineRules: Map>, - ruleSets: Iterable + baselineRules: Map> ) { val (excludedDirs, excludedFiles) = excludes.map(::File).partition { it.isDirectory } directory @@ -222,8 +218,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { baselineRules.getOrDefault( file.relativeTo(mavenProject.basedir.parentFile).invariantSeparatorsPath, emptyList() - ), - ruleSets + ) ) reporterImpl.after(file.absolutePath) } catch (e: RuleExecutionException) { @@ -233,26 +228,26 @@ abstract class DiktatBaseMojo : AbstractMojo() { } } - private fun checkFile(file: File, - lintErrors: MutableList, - baselineErrors: List, - ruleSets: Iterable + private fun checkFile( + file: File, + lintErrors: MutableList, + baselineErrors: List ) { - val text = file.readText() - val params = - KtLint.ExperimentalParams( - fileName = file.absolutePath, - text = text, - ruleSets = ruleSets, - script = file.extension.equals("kts", ignoreCase = true), - cb = { lintError, isCorrected -> - if (!baselineErrors.containsLintError(lintError)) { - reporterImpl.onLintError(file.absolutePath, lintError, isCorrected) - lintErrors.add(lintError) - } - }, - debug = debug + val command = DiktatProcessCommand.Builder() + .file(file.toPath()) + .fileContent(file.readText(Charsets.UTF_8)) + .isScript(file.extension.equals("kts", ignoreCase = true)) + .callback { error, isCorrected -> + val ktLintError = error.unwrap() + if (!baselineErrors.containsLintError(ktLintError)) { + reporterImpl.onLintError(file.absolutePath, ktLintError, isCorrected) + lintErrors.add(ktLintError) + } + } + .logLevel( + if (debug) DiktatLogLevel.DEBUG else DiktatLogLevel.INFO ) - runAction(params) + .build() + runAction(command) } } diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt index 1ff3f810c0..e14ef00a07 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt @@ -4,13 +4,12 @@ package org.cqfn.diktat.plugin.maven +import org.cqfn.diktat.DiktatProcessCommand import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import org.cqfn.diktat.ruleset.utils.ignoreCorrectedErrors -import com.pinterest.ktlint.core.KtLint import org.apache.maven.plugins.annotations.Mojo - -import java.io.File +import kotlin.io.path.absolutePathString +import kotlin.io.path.writeText /** * Main [Mojo] that call [DiktatRuleSetProvider]'s rules on [inputs] files @@ -19,10 +18,10 @@ import java.io.File @Suppress("unused") class DiktatCheckMojo : DiktatBaseMojo() { /** - * @param params instance of [KtLint.ExperimentalParams] used in analysis + * @param command instance of [DiktatProcessCommand] used in analysis */ - override fun runAction(params: KtLint.ExperimentalParams) { - KtLint.lint(params) + override fun runAction(command: DiktatProcessCommand) { + command.check() } } @@ -34,15 +33,15 @@ class DiktatCheckMojo : DiktatBaseMojo() { @Suppress("unused") class DiktatFixMojo : DiktatBaseMojo() { /** - * @param params instance of [KtLint.Params] used in analysis + * @param command instance of [DiktatProcessCommand] used in analysis */ - override fun runAction(params: KtLint.ExperimentalParams) { - val fileName = params.fileName - val fileContent = File(fileName).readText(charset("UTF-8")) - val formattedText = KtLint.format(params.ignoreCorrectedErrors()) + override fun runAction(command: DiktatProcessCommand) { + val fileName = command.file.absolutePathString() + val fileContent = command.fileContent + val formattedText = command.fix() if (fileContent != formattedText) { log.info("Original and formatted content differ, writing to $fileName...") - File(fileName).writeText(formattedText, charset("UTF-8")) + command.file.writeText(formattedText, Charsets.UTF_8) } } } diff --git a/diktat-ruleset/pom.xml b/diktat-ruleset/pom.xml index 9024648b37..de6d80c22e 100644 --- a/diktat-ruleset/pom.xml +++ b/diktat-ruleset/pom.xml @@ -19,6 +19,7 @@ ${project.version} + org.jetbrains.kotlin kotlin-stdlib-common @@ -47,27 +48,27 @@ org.jetbrains.kotlin kotlin-stdlib-common - test + provided org.jetbrains.kotlin kotlin-stdlib-jdk7 - test + provided org.jetbrains.kotlin kotlin-stdlib-jdk8 - test + provided org.jetbrains.kotlin kotlin-stdlib - test + provided org.jetbrains.kotlin kotlin-compiler-embeddable - test + provided org.junit.jupiter @@ -98,6 +99,20 @@ org.jetbrains.kotlin kotlin-maven-plugin + + compile + process-sources + + compile + + + + src/main/java + src/main/kotlin + src/main/resources + + + test-compile process-test-sources @@ -116,16 +131,6 @@ - - - maven-jar-plugin - - - default-jar - none - - - org.apache.maven.plugins diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt new file mode 100644 index 0000000000..c2667014ab --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -0,0 +1,121 @@ +package org.cqfn.diktat + +import org.cqfn.diktat.api.DiktatCallback +import org.cqfn.diktat.api.DiktatLogLevel +import org.cqfn.diktat.ktlint.unwrap +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import com.pinterest.ktlint.core.KtLint +import com.pinterest.ktlint.core.api.EditorConfigDefaults +import com.pinterest.ktlint.core.api.EditorConfigOverride +import java.nio.file.Path +import kotlin.io.path.absolutePathString + +/** + * Command to run `diktat` + * + * @property file + * @property fileContent + */ +class DiktatProcessCommand private constructor( + val file: Path, + val fileContent: String, + private val callback: DiktatCallback, + private val isScript: Boolean, + private val logLevel: DiktatLogLevel, +) { + /** + * Run `diktat fix` using parameters from current command + * + * @return result of `diktat fix` + */ + fun fix(): String = KtLint.format(ktLintParams()) + + /** + * Run `diktat check` using parameters from current command + */ + fun check() { + KtLint.lint(ktLintParams()) + } + + @Suppress("DEPRECATION") + private fun ktLintParams(): KtLint.ExperimentalParams = KtLint.ExperimentalParams( + fileName = file.absolutePathString(), + text = fileContent, + ruleSets = setOf(DiktatRuleSetProvider().get()), + ruleProviders = emptySet(), + userData = emptyMap(), + cb = callback.unwrap(), + script = isScript, + editorConfigPath = null, + debug = logLevel == DiktatLogLevel.DEBUG, + editorConfigDefaults = EditorConfigDefaults.emptyEditorConfigDefaults, + editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride, + isInvokedFromCli = false + ) + + /** + * Builder for [DiktatProcessCommand] + * + * @property file + * @property fileContent + * @property callback + * @property isScript + * @property logLevel + */ + data class Builder( + var file: Path? = null, + var fileContent: String? = null, + var callback: DiktatCallback? = null, + var isScript: Boolean? = null, + var logLevel: DiktatLogLevel = DiktatLogLevel.INFO, + ) { + /** + * @param file + * @return updated builder + */ + fun file(file: Path) = apply { this.file = file } + + /** + * @param fileContent + * @return updated builder + */ + fun fileContent(fileContent: String) = apply { this.fileContent = fileContent } + + /** + * @param callback + * @return updated builder + */ + fun callback(callback: DiktatCallback) = apply { this.callback = callback } + + /** + * @param isScript + * @return updated builder + */ + fun isScript(isScript: Boolean) = apply { this.isScript = isScript } + + /** + * @param logLevel + * @return updated builder + */ + fun logLevel(logLevel: DiktatLogLevel) = apply { this.logLevel = logLevel } + + /** + * @return built [DiktatProcessCommand] + */ + fun build() = DiktatProcessCommand( + requireNotNull(file) { + "file is required" + }, + requireNotNull(fileContent) { + "fileContent is required" + }, + requireNotNull(callback) { + "callback is required" + }, + requireNotNull(isScript) { + "isScript is required" + }, + logLevel, + ) + } +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt new file mode 100644 index 0000000000..ba4141c9c8 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt @@ -0,0 +1,17 @@ +package org.cqfn.diktat.api + +import java.util.function.BiConsumer + +/** + * Callback for diktat process + */ +@FunctionalInterface +fun interface DiktatCallback : BiConsumer { + /** + * Performs this callback on the given [error] taking into account [isCorrected] flag. + * + * @param error the error found by diktat + * @param isCorrected true if the error fixed by diktat + */ + override fun accept(error: DiktatError, isCorrected: Boolean) +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt new file mode 100644 index 0000000000..3d4aac4758 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt @@ -0,0 +1,31 @@ +package org.cqfn.diktat.api + +/** + * Error found by `diktat` + */ +interface DiktatError { + /** + * @return line number (one-based) + */ + fun getLine(): Int + + /** + * @return column number (one-based) + */ + fun getCol(): Int + + /** + * @return rule id + */ + fun getRuleId(): String + + /** + * @return error message + */ + fun getDetail(): String + + /** + * @return true if the found error can be fixed + */ + fun canBeAutoCorrected(): Boolean +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt new file mode 100644 index 0000000000..6e6e18641d --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt @@ -0,0 +1,10 @@ +package org.cqfn.diktat.api + +/** + * Log level of `diktat processing` + */ +enum class DiktatLogLevel { + DEBUG, + INFO, + ; +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt new file mode 100644 index 0000000000..e768caebe2 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt @@ -0,0 +1,22 @@ +/** + * This file contains utility methods for LintErrorCallback + */ + +package org.cqfn.diktat.ktlint + +import org.cqfn.diktat.api.DiktatCallback +import org.cqfn.diktat.ruleset.utils.LintErrorCallback + +/** + * @return [DiktatCallback] from KtLint [LintErrorCallback] + */ +fun LintErrorCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> + this(error.unwrap(), isCorrected) +} + +/** + * @return KtLint [LintErrorCallback] from [DiktatCallback] or exception + */ +fun DiktatCallback.unwrap(): LintErrorCallback = { error, isCorrected -> + this.accept(error.wrap(), isCorrected) +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt new file mode 100644 index 0000000000..b293a1cee5 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt @@ -0,0 +1,34 @@ +package org.cqfn.diktat.ktlint + +import org.cqfn.diktat.api.DiktatError +import com.pinterest.ktlint.core.LintError + +/** + * Wrapper for KtLint error + * + * @property lintError + */ +data class LintErrorWrapper( + val lintError: LintError +) : DiktatError { + override fun getLine(): Int = lintError.line + + override fun getCol(): Int = lintError.col + + override fun getRuleId(): String = lintError.ruleId + + override fun getDetail(): String = lintError.detail + + override fun canBeAutoCorrected(): Boolean = lintError.canBeAutoCorrected +} + +/** + * @return [DiktatError] from KtLint [LintError] + */ +fun LintError.wrap(): DiktatError = LintErrorWrapper(this) + +/** + * @return KtLint [LintError] from [DiktatError] or exception + */ +fun DiktatError.unwrap(): LintError = (this as? LintErrorWrapper)?.lintError + ?: error("Unsupported wrapper of ${DiktatError::class.java.simpleName}: ${this::class.java.canonicalName}") From a2ba01abef61ad69c4e355f9e400ee5cb7b1c8a4 Mon Sep 17 00:00:00 2001 From: Andrey Shcheglov Date: Fri, 18 Nov 2022 16:30:54 +0300 Subject: [PATCH 03/33] Migrate to the new rule set API (#1570) ### What's done: - Deprecated API usages removed (except for `DiktatBaseMojo`). - @Suppress("Deprecation") annotations removed (except for `DiktatBaseMojo`). - A part of #1543. --- .../diktat/plugin/gradle/DiktatExtension.kt | 2 +- .../diktat/plugin/maven/DiktatBaseMojo.kt | 5 + .../cqfn/diktat/plugin/maven/DiktatMojo.kt | 6 +- .../diktat/plugin/maven/DiktatBaseMojoTest.kt | 2 - .../diktat/ruleset/rules/DiktatRuleSet.kt | 14 ++ .../ruleset/rules/DiktatRuleSetFactory.kt | 40 ++++++ .../ruleset/rules/DiktatRuleSetProvider.kt | 39 ++---- .../ruleset/rules/DiktatRuleSetProviderV2.kt | 34 +++++ .../diktat/ruleset/rules/OrderedRuleSet.kt | 66 ++++----- .../ruleset/rules/chapter3/EnumsSeparated.kt | 3 +- .../rules/chapter3/files/IndentationRule.kt | 5 +- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 47 ++++++- .../cqfn/diktat/ruleset/utils/KtlintUtils.kt | 9 +- .../com.pinterest.ktlint.core.RuleSetProvider | 1 - ...om.pinterest.ktlint.core.RuleSetProviderV2 | 1 + .../junit/IndentationTestWarnExtension.kt | 7 +- .../diktat/ruleset/junit/CloseablePath.kt | 43 +----- .../ruleset/rules/OrderedRuleSetTest.kt | 13 +- .../smoke/RulesConfigValidationTest.kt | 11 +- .../diktat/ruleset/utils/AstNodeUtilsTest.kt | 12 +- .../diktat/util/DiktatRuleSetProvider4Test.kt | 75 +++++----- .../org/cqfn/diktat/util/FixTestBase.kt | 4 - .../org/cqfn/diktat/util/LintTestBase.kt | 5 +- .../kotlin/org/cqfn/diktat/util/TestUtils.kt | 17 +-- .../org/cqfn/diktat/DiktatProcessCommand.kt | 13 +- .../ruleset/smoke/DiktatSaveSmokeTest.kt | 4 +- .../diktat/ruleset/smoke/DiktatSmokeTest.kt | 6 +- .../org/cqfn/diktat/util/SmokeTestUtils.kt | 77 ----------- .../diktat/test/framework/util/TestUtils.kt | 129 ++++++++++++++++++ .../diktat/generation/docs/GenerationDocs.kt | 4 +- 30 files changed, 412 insertions(+), 282 deletions(-) create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSet.kt create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt delete mode 100644 diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider create mode 100644 diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 delete mode 100644 diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt index 5b939f8f3e..e25eb42468 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt @@ -58,6 +58,6 @@ open class DiktatExtension( * @param action configuration lambda for `PatternFilterable` */ fun inputs(action: PatternFilterable.() -> Unit) { - action.invoke(patternSet) + action(patternSet) } } diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 878b822ea6..3c29da0ac4 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -222,6 +222,11 @@ abstract class DiktatBaseMojo : AbstractMojo() { ) reporterImpl.after(file.absolutePath) } catch (e: RuleExecutionException) { + /* + * https://github.com/pinterest/ktlint/issues/1710: + * no alternative in KtLint 0.47; + * may get changed to `KtLintRuleExecutionException` in KtLint 0.48. + */ log.error("Unhandled exception during rule execution: ", e) throw MojoExecutionException("Unhandled exception during rule execution", e) } diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt index e14ef00a07..bc230de2b6 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt @@ -5,14 +5,14 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import org.apache.maven.plugins.annotations.Mojo import kotlin.io.path.absolutePathString import kotlin.io.path.writeText /** - * Main [Mojo] that call [DiktatRuleSetProvider]'s rules on [inputs] files + * Main [Mojo] that call [DiktatRuleSetProviderV2]'s rules on [inputs] files */ @Mojo(name = "check") @Suppress("unused") @@ -26,7 +26,7 @@ class DiktatCheckMojo : DiktatBaseMojo() { } /** - * Main [Mojo] that call [DiktatRuleSetProvider]'s rules on [inputs] files + * Main [Mojo] that call [DiktatRuleSetProviderV2]'s rules on [inputs] files * and fixes discovered errors */ @Mojo(name = "fix") diff --git a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt index ea1505fda8..5f76547344 100644 --- a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt +++ b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt @@ -10,7 +10,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.jupiter.api.Assertions -import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path import kotlin.io.path.createTempFile import kotlin.io.path.div @@ -18,7 +17,6 @@ import kotlin.io.path.div /** * Tests for mojo configuration. NB: this tests are using Junit4, because maven-plugin-testing-harness doesn't support 5. */ -@OptIn(ExperimentalPathApi::class) @Suppress("LongMethod", "TOO_LONG_FUNCTION") class DiktatBaseMojoTest { @get:Rule val mojoRule = MojoRule() diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSet.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSet.kt new file mode 100644 index 0000000000..e3943ab0c9 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSet.kt @@ -0,0 +1,14 @@ +package org.cqfn.diktat.ruleset.rules + +import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 + +/** + * A group of [RuleProvider]'s discoverable through [RuleSetProviderV2]. + */ +interface DiktatRuleSet { + /** + * The rule providers. + */ + val ruleProviders: Set +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt new file mode 100644 index 0000000000..061ed87598 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt @@ -0,0 +1,40 @@ +package org.cqfn.diktat.ruleset.rules + +import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF +import com.pinterest.ktlint.core.KtLint +import com.pinterest.ktlint.core.Rule +import kotlin.Function0 + +/** + * _KtLint_-agnostic factory which creates a [DiktatRuleSet]. + */ +fun interface DiktatRuleSetFactory : Function0 { + /** + * This method is going to be called once for each file (which means if any + * of the rules have state or are not thread-safe - a new [DiktatRuleSet] must + * be created). + * + * For each invocation of [KtLint.lint] and [KtLint.format] the [DiktatRuleSet] + * is retrieved. + * This results in new instances of each [Rule] for each file being + * processed. + * As of that a [Rule] does not need to be thread-safe. + * + * However, [KtLint.format] requires the [Rule] to be executed twice on a + * file in case at least one violation has been autocorrected. + * As the same [Rule] instance is reused for the second execution of the + * [Rule], the state of the [Rule] is shared. + * As of this [Rule] have to clear their internal state. + */ + override fun invoke(): DiktatRuleSet + + companion object { + /** + * @param diktatConfigFile the configuration file where all configurations for + * inspections and rules are stored. + * @return a new instance of [DiktatRuleSetFactory]. + */ + operator fun invoke(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): DiktatRuleSetFactory = + DiktatRuleSetProvider(diktatConfigFile) + } +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt index 2dbe192b50..a9f252884c 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt @@ -1,7 +1,3 @@ -@file:Suppress( - "Deprecation" -) - package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF @@ -12,7 +8,6 @@ import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.RulesConfigReader import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import org.cqfn.diktat.ruleset.constants.Warnings -import org.cqfn.diktat.ruleset.rules.OrderedRuleSet.Companion.ordered import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming @@ -88,26 +83,19 @@ import org.cqfn.diktat.ruleset.rules.chapter6.classes.SingleConstructorRule import org.cqfn.diktat.ruleset.rules.chapter6.classes.SingleInitRule import org.cqfn.diktat.ruleset.rules.chapter6.classes.StatelessClassesRule -import com.pinterest.ktlint.core.RuleSet -import com.pinterest.ktlint.core.RuleSetProvider import mu.KotlinLogging import org.jetbrains.kotlin.org.jline.utils.Levenshtein import java.io.File /** - * [RuleSetProvider] that provides diKTat ruleset. - * By default, it is expected to have diktat-analysis.yml configuration in the root folder where 'ktlint' is run + * By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run * otherwise it will use default configuration where some rules are disabled. * - * The no-argument constructor is used by the Java SPI interface; that's why - * it's explicitly annotated with [JvmOverloads]. - * - * @param diktatConfigFile - configuration file where all configurations for inspections and rules are stored + * @param diktatConfigFile the configuration file where all configurations for + * inspections and rules are stored. */ -class DiktatRuleSetProvider -@JvmOverloads -constructor(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleSetProvider { +internal class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : DiktatRuleSetFactory { private val possibleConfigs: Sequence = sequence { yield(resolveDefaultConfig()) yield(resolveConfigFileFromJarLocation()) @@ -143,14 +131,11 @@ constructor(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleS "LongMethod", "TOO_LONG_FUNCTION", ) - @Deprecated( - "Marked for removal in KtLint 0.48. See changelog or KDoc for more information.", - ) - override fun get(): RuleSet { + override fun invoke(): DiktatRuleSet { // Note: the order of rules is important in autocorrect mode. For example, all rules that add new code should be invoked before rules that fix formatting. // We don't have a way to enforce a specific order, so we should just be careful when adding new rules to this list and, when possible, // cover new rules in smoke test as well. If a rule needs to be at a specific position in a list, please add comment explaining it (like for NewlinesRule). - val rules = listOf( + val rules = sequenceOf( // comments & documentation ::CommentsRule, ::SingleConstructorRule, // this rule can add properties to a primary constructor, so should be before KdocComments @@ -233,12 +218,13 @@ constructor(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleS ) .map { - it.invoke(configRules) + it(configRules) } - return RuleSet( + .toList() + return OrderedRuleSet( DIKTAT_RULE_SET_ID, - rules = rules.toTypedArray() - ).ordered() + rules + ) } private fun validate(config: RulesConfig) = @@ -253,8 +239,7 @@ constructor(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleS // for some aggregators of static analyzers we need to provide configuration for cli // in this case diktat would take the configuration from the directory where jar file is stored val ruleSetProviderPath = - DiktatRuleSetProvider::class - .java + javaClass .protectionDomain .codeSource .location diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt new file mode 100644 index 0000000000..c8befe4b84 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt @@ -0,0 +1,34 @@ +package org.cqfn.diktat.ruleset.rules + +import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID +import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 + +/** + * [RuleSetProviderV2] that provides diKTat ruleset. + * + * The no-argument constructor is used by the Java SPI interface; that's why + * it's explicitly annotated with [JvmOverloads]. + */ +@Suppress("serial") +class DiktatRuleSetProviderV2 +@JvmOverloads +constructor(private val factory: DiktatRuleSetFactory = DiktatRuleSetFactory()) : RuleSetProviderV2( + id = DIKTAT_RULE_SET_ID, + about = About( + maintainer = "Diktat", + description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", + license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", + repositoryUrl = "https://github.com/saveourtool/diktat", + issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", + ), +) { + /** + * @param diktatConfigFile the configuration file where all configurations for + * inspections and rules are stored. + */ + constructor(diktatConfigFile: String) : this(DiktatRuleSetFactory(diktatConfigFile)) + + override fun getRuleProviders(): Set = + factory().ruleProviders +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSet.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSet.kt index 7040128b60..4de03dd36d 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSet.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSet.kt @@ -1,13 +1,9 @@ -@file:Suppress( - "Deprecation" -) - package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId import org.cqfn.diktat.ruleset.constants.EmitType import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSet +import com.pinterest.ktlint.core.RuleProvider import com.pinterest.ktlint.core.api.EditorConfigProperties import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -15,14 +11,42 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode * This is a wrapper around Ktlint RuleSet which adjusts visitorModifiers for all rules to keep order with prevRule * Added as a workaround after introducing a new logic for sorting KtLint Rules: https://github.com/pinterest/ktlint/issues/1478 * - * @param id ID of RuleSet - * @param rules rules which belongs to current RuleSet + * @param rules the rules which belong to the current [DiktatRuleSet]. */ -class OrderedRuleSet(id: String, vararg rules: Rule) : RuleSet(id, rules = adjustRules(id, rules = rules)) { +class OrderedRuleSet private constructor( + private val rules: List +) : DiktatRuleSet { + @Suppress("CUSTOM_GETTERS_SETTERS") + override val ruleProviders: Set + get() = + rules.map { rule -> + RuleProvider { + rule + } + }.toSet() + + /** + * @param id the ID of the [DiktatRuleSet]. + * @param rules the rules which belong to the current [DiktatRuleSet]. + */ + constructor( + id: String, + rules: List + ) : this(id, rules = rules.toTypedArray()) + + /** + * @param id the ID of the [DiktatRuleSet]. + * @param rules the rules which belong to the current [DiktatRuleSet]. + */ + constructor( + id: String, + vararg rules: Rule + ) : this(adjustRules(id, rules)) + companion object { - private fun adjustRules(ruleSetId: String, vararg rules: Rule): Array { + private fun adjustRules(ruleSetId: String, rules: Array): List { if (rules.isEmpty()) { - return emptyArray() + return emptyList() } return rules.mapIndexed { index, rule -> if (index == 0) { @@ -31,7 +55,7 @@ class OrderedRuleSet(id: String, vararg rules: Rule) : RuleSet(id, rules = adjus } else { OrderedRule(ruleSetId, rule, rules[index - 1]) } - }.toTypedArray() + }.toList() } private fun adjustVisitorModifiers( @@ -64,15 +88,6 @@ class OrderedRuleSet(id: String, vararg rules: Rule) : RuleSet(id, rules = adjus */ internal fun Rule.delegatee(): Rule = if (this is OrderedRule) this.rule else this - /** - * @return RuleSet with ordered rules - */ - fun RuleSet.ordered(): RuleSet = - when (this) { - is OrderedRuleSet -> this - else -> OrderedRuleSet(id = id, rules = rules) - } - /** * @property rule wraps this rule to keep order */ @@ -91,17 +106,6 @@ class OrderedRuleSet(id: String, vararg rules: Rule) : RuleSet(id, rules = adjus ) = rule.beforeVisitChildNodes(node, autoCorrect, emit) - @Deprecated( - "Marked for deletion in ktlint 0.48.0", - replaceWith = ReplaceWith("beforeVisitChildNodes(node, autoCorrect, emit)"), - ) - override fun visit( - node: ASTNode, - autoCorrect: Boolean, - emit: EmitType, - ) = - rule.visit(node, autoCorrect, emit) - override fun afterVisitChildNodes( node: ASTNode, autoCorrect: Boolean, diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt index 743fd2ab75..95ee973046 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt @@ -3,6 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter3 import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.ruleset.constants.Warnings.ENUMS_SEPARATED import org.cqfn.diktat.ruleset.rules.DiktatRule +import org.cqfn.diktat.ruleset.utils.AstNodePredicate import org.cqfn.diktat.ruleset.utils.allSiblings import org.cqfn.diktat.ruleset.utils.appendNewlineMergingWhiteSpace import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType @@ -106,7 +107,7 @@ class EnumsSeparated(configRules: List) : DiktatRule( } } - private fun ASTNode.findLatestTreePrevMatching(predicate: (ASTNode) -> Boolean): ASTNode { + private fun ASTNode.findLatestTreePrevMatching(predicate: AstNodePredicate): ASTNode { val result = this.treePrev return if (predicate(result)) result else result.findLatestTreePrevMatching(predicate) } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt index 36e731a0e3..773ff860ac 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt @@ -36,6 +36,7 @@ import org.cqfn.diktat.ruleset.utils.indentation.ValueParameterListChecker import org.cqfn.diktat.ruleset.utils.lastIndent import org.cqfn.diktat.ruleset.utils.leadingSpaceCount import org.cqfn.diktat.ruleset.utils.leaveOnlyOneNewLine +import org.cqfn.diktat.ruleset.utils.visit import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION @@ -66,7 +67,6 @@ import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline -import com.pinterest.ktlint.core.ast.visit import mu.KotlinLogging import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace @@ -121,7 +121,7 @@ class IndentationRule(configRules: List) : DiktatRule( ::KdocIndentationChecker, ::CustomGettersAndSettersChecker, ::ArrowInWhenChecker - ).map { it.invoke(configuration) } + ).map { it(configuration) } if (checkIsIndentedWithSpaces(node)) { checkIndentation(node) @@ -187,7 +187,6 @@ class IndentationRule(configRules: List) : DiktatRule( */ private fun checkIndentation(node: ASTNode) = with(IndentContext(configuration)) { - @Suppress("Deprecation") node.visit { astNode -> checkAndReset(astNode) val indentationIncrement = astNode.getIndentationIncrement() diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index b396daae2d..439f3efd40 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -74,6 +74,16 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings import java.util.Locale +/** + * AST node visitor which accepts the node and, optionally, returns a value. + */ +typealias AstNodeVisitor = (node: ASTNode) -> T + +/** + * The predicate which accepts a node and returns a `boolean` value. + */ +typealias AstNodePredicate = AstNodeVisitor + /** * A class that represents result of nodes swapping. [oldNodes] should always have same size as [newNodes] * @@ -449,8 +459,8 @@ fun ASTNode.findAllDescendantsWithSpecificType(elementType: IElementType, withSe * This method performs tree traversal and returns all nodes which satisfy the condition */ fun ASTNode.findAllNodesWithCondition(withSelf: Boolean = true, - excludeChildrenCondition: ((ASTNode) -> Boolean) = { false }, - condition: (ASTNode) -> Boolean, + excludeChildrenCondition: AstNodePredicate = { false }, + condition: AstNodePredicate, ): List { val result = if (condition(this) && withSelf) mutableListOf(this) else mutableListOf() return result + this.getChildren(null) @@ -481,7 +491,7 @@ fun ASTNode.findChildrenMatching(elementType: IElementType? = null, * Check if this node has any children of optional type matching the predicate */ fun ASTNode.hasChildMatching(elementType: IElementType? = null, - predicate: (ASTNode) -> Boolean): Boolean = + predicate: AstNodePredicate): Boolean = findChildrenMatching(elementType, predicate).isNotEmpty() /** @@ -776,7 +786,7 @@ fun ASTNode.findAllNodesOnLine( */ fun ASTNode.findAllNodesWithConditionOnLine( line: Int, - condition: (ASTNode) -> Boolean + condition: AstNodePredicate ): List? = this.findAllNodesOnLine(line)?.filter(condition) /** @@ -890,6 +900,35 @@ fun ASTNode.isBooleanExpression(): Boolean = } } +/** + * Before _KtLint_ **0.48**, this extension used to reside in the + * `com.pinterest.ktlint.core.ast` package. + * Because _KtLint_ **0.47** has changed the way syntax nodes are traversed (see + * the diagrams [here](https://github.com/saveourtool/diktat/issues/1538)), the + * codebase of _KtLint_ no longer needs it: in most cases, it's sufficient to + * invoke + * + * ```kotlin + * visitor(this) + * ``` + * + * and rely on _KtLint_ to invoke the same visitor (which is usually a `Rule`) + * on this node's children. + * Still, _Diktat_ sometimes needs exactly this old behaviour. + * + * @param visitor the visitor to recursively traverse this node as well as its + * children. + * @since 1.2.5 + */ +fun ASTNode.visit(visitor: AstNodeVisitor) { + visitor(this) + @Suppress("NULLABLE_PROPERTY_TYPE") + val filter: TokenSet? = null + getChildren(filter).forEach { child -> + child.visit(visitor) + } +} + /** * @return whether this PSI element is a long string template entry. * @since 1.2.4 diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt index 6d773bd279..9f74ede292 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt @@ -1,6 +1,5 @@ @file:Suppress( "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", - "Deprecation", ) package org.cqfn.diktat.ruleset.utils @@ -9,7 +8,7 @@ import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.KtLint.ExperimentalParams import com.pinterest.ktlint.core.LintError -import com.pinterest.ktlint.core.RuleSetProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 import mu.KotlinLogging import org.intellij.lang.annotations.Language @@ -54,16 +53,16 @@ fun ExperimentalParams.ignoreCorrectedErrors(): ExperimentalParams = */ @Suppress("LAMBDA_IS_NOT_LAST_PARAMETER") fun format( - ruleSetProviderRef: () -> RuleSetProvider, + ruleSetProviderRef: () -> RuleSetProviderV2, @Language("kotlin") text: String, fileName: String, cb: LintErrorCallback = defaultCallback ): String { - val ruleSets = listOf(ruleSetProviderRef().get()) + val ruleProviders = ruleSetProviderRef().getRuleProviders() return KtLint.format( ExperimentalParams( text = text, - ruleSets = ruleSets, + ruleProviders = ruleProviders, fileName = fileName.removeSuffix("_copy"), script = fileName.removeSuffix("_copy").endsWith("kts"), cb = cb, diff --git a/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider b/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider deleted file mode 100644 index f87bbe5d1f..0000000000 --- a/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider +++ /dev/null @@ -1 +0,0 @@ -org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider diff --git a/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 b/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 new file mode 100644 index 0000000000..9794d26cc2 --- /dev/null +++ b/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 @@ -0,0 +1 @@ +org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestWarnExtension.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestWarnExtension.kt index cac665b66a..1f6af72694 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestWarnExtension.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestWarnExtension.kt @@ -19,15 +19,12 @@ import kotlin.math.min * @property actualCode the original file content (may well get modified as * fixes are applied). */ -@Suppress( - "TOO_MANY_BLANK_LINES", // Readability - "WRONG_INDENTATION") // False positives, see #1404. +@Suppress("TOO_MANY_BLANK_LINES") // Readability internal class IndentationTestWarnExtension( override val customConfig: Map, @Language("kotlin") override val actualCode: String, private val expectedErrors: Array -) : LintTestBase(::IndentationRule), - IndentationTestExtension { +) : LintTestBase(::IndentationRule), IndentationTestExtension { override fun beforeTestExecution(context: ExtensionContext) { val actualErrors = lintResult( diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/junit/CloseablePath.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/junit/CloseablePath.kt index 8141cefc64..a0fc9fa6c0 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/junit/CloseablePath.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/junit/CloseablePath.kt @@ -1,5 +1,7 @@ package org.cqfn.diktat.ruleset.junit +import org.cqfn.diktat.test.framework.util.resetPermissions +import org.cqfn.diktat.test.framework.util.tryToDeleteOnExit import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource import java.io.IOException import java.nio.file.DirectoryNotEmptyException @@ -15,6 +17,7 @@ import kotlin.io.path.absolute import kotlin.io.path.deleteExisting import kotlin.io.path.isDirectory import kotlin.io.path.notExists +import kotlin.io.path.relativeToOrSelf /** * @property directory the temporary directory (will be recursively deleted once @@ -102,11 +105,11 @@ data class CloseablePath(val directory: Path) : CloseableResource { @Suppress("WRONG_NEWLINES") // False positives, see #1495. val joinedPaths = keys .asSequence() + .map(Path::tryToDeleteOnExit) .map { path -> - path.tryToDeleteOnExit() - }.map { path -> - path.relativizeSafely() - }.map(Any::toString) + path.relativeToOrSelf(directory) + } + .map(Any::toString) .joinToString() return IOException("Failed to delete temp directory ${directory.absolute()}. " + @@ -114,36 +117,4 @@ data class CloseablePath(val directory: Path) : CloseableResource { values.forEach(this::addSuppressed) } } - - private fun Path.tryToDeleteOnExit(): Path { - try { - toFile().deleteOnExit() - } catch (_: UnsupportedOperationException) { - /* - * Ignore. - */ - } - - return this - } - - private fun Path.relativizeSafely(): Path = - try { - directory.relativize(this) - } catch (_: IllegalArgumentException) { - this - } - - private companion object { - private fun Path.resetPermissions() { - toFile().apply { - setReadable(true) - setWritable(true) - - if (isDirectory) { - setExecutable(true) - } - } - } - } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt index 83112cc15b..6944a30242 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt @@ -5,6 +5,7 @@ import org.cqfn.diktat.ruleset.constants.EmitType import org.cqfn.diktat.util.TEST_FILE_NAME import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.RuleProvider import org.assertj.core.api.Assertions.assertThat import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -39,9 +40,9 @@ class OrderedRuleSetTest { val orderedRuleSet = OrderedRuleSet(ruleSetId, rule1, rule2) - val orderedRuleSetIterator = orderedRuleSet.iterator() - val orderedRule1 = orderedRuleSetIterator.next() - val orderedRule2 = orderedRuleSetIterator.next() + val orderedRuleSetIterator = orderedRuleSet.ruleProviders.iterator() + val orderedRule1 = orderedRuleSetIterator.next().createNewRuleInstance() + val orderedRule2 = orderedRuleSetIterator.next().createNewRuleInstance() Assertions.assertFalse(orderedRuleSetIterator.hasNext(), "Extra elements after ordering") Assertions.assertEquals(rule1, orderedRule1, "First rule is modified") @@ -85,8 +86,8 @@ class OrderedRuleSetTest { /* * Make sure OrderedRuleSet preserves the order. */ - val ruleSet = OrderedRuleSet(ruleSetId, *rules.toTypedArray()) - assertThat(ruleSet.rules.map(Rule::id)).containsExactlyElementsOf(rules.map(Rule::id)) + val ruleSet = OrderedRuleSet(ruleSetId, rules) + assertThat(ruleSet.ruleProviders.map(RuleProvider::createNewRuleInstance).map(Rule::id)).containsExactlyElementsOf(rules.map(Rule::id)) @Language("kotlin") val code = "fun foo() { }" @@ -95,7 +96,7 @@ class OrderedRuleSetTest { KtLint.ExperimentalParams( fileName = TEST_FILE_NAME, text = code, - ruleSets = listOf(ruleSet), + ruleProviders = ruleSet.ruleProviders, cb = { _, _ -> }, ) ) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt index 07e2135a97..3afd7164fe 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt @@ -1,6 +1,6 @@ package org.cqfn.diktat.ruleset.smoke -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently import com.charleskorn.kaml.InvalidPropertyValueException @@ -39,8 +39,7 @@ class RulesConfigValidationTest { """.trimMargin() ) val exception = assertThrows { - @Suppress("DEPRECATION") - DiktatRuleSetProvider(file.absolutePath).get() + DiktatRuleSetProviderV2(file.absolutePath).getRuleProviders() } Assertions.assertEquals("Warning name in configuration file is invalid, did you mean ?", exception.message) } @@ -55,8 +54,7 @@ class RulesConfigValidationTest { """.trimMargin() ) assertThrows { - @Suppress("DEPRECATION") - DiktatRuleSetProvider(file.absolutePath).get() + DiktatRuleSetProviderV2(file.absolutePath).getRuleProviders() } } @@ -72,7 +70,6 @@ class RulesConfigValidationTest { | isIncludeHeader: Fslse """.trimMargin() ) - @Suppress("DEPRECATION") - DiktatRuleSetProvider(file.absolutePath).get() + DiktatRuleSetProviderV2(file.absolutePath).getRuleProviders() } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt index 0adc251de5..3647aa16f3 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt @@ -2,7 +2,6 @@ "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "LOCAL_VARIABLE_EARLY_DECLARATION", "AVOID_NULL_CHECKS", - "Deprecation", ) package org.cqfn.diktat.ruleset.utils @@ -12,7 +11,7 @@ import org.cqfn.diktat.util.applyToCode import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSet +import com.pinterest.ktlint.core.RuleProvider import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.ElementType.CLASS import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY @@ -31,6 +30,7 @@ import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.isLeaf import com.pinterest.ktlint.core.ast.nextCodeSibling import com.pinterest.ktlint.core.ast.nextSibling +import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType @@ -791,7 +791,7 @@ private class PrettyPrintingVisitor(private val elementType: IElementType, private val level: Int, private val maxLevel: Int, private val expected: String -) : Rule("print-ast") { +) : Rule("test:print-ast") { override fun beforeVisitChildNodes(node: ASTNode, autoCorrect: Boolean, emit: EmitType @@ -807,7 +807,7 @@ private class PrettyPrintingVisitor(private val elementType: IElementType, companion object { fun assertStringRepr( elementType: IElementType, - code: String, + @Language("kotlin") code: String, level: Int = 0, maxLevel: Int = -1, expected: String @@ -815,7 +815,9 @@ private class PrettyPrintingVisitor(private val elementType: IElementType, KtLint.lint( KtLint.ExperimentalParams( text = code, - ruleSets = listOf(RuleSet("test", PrettyPrintingVisitor(elementType, level, maxLevel, expected))), + ruleProviders = setOf(RuleProvider { + PrettyPrintingVisitor(elementType, level, maxLevel, expected) + }), cb = { _, _ -> } ) ) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt index 6e9918cff8..c5297db80c 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt @@ -2,65 +2,62 @@ * Stub for diktat ruleset provide to be used in tests and other related utilities */ -@file:Suppress( - "Deprecation" -) - package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.RulesConfigReader -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import org.cqfn.diktat.ruleset.rules.OrderedRuleSet.Companion.delegatee +import org.cqfn.diktat.test.framework.util.filterContentMatches import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSet -import com.pinterest.ktlint.core.RuleSetProvider +import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import java.io.File +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.isRegularFile +import kotlin.io.path.nameWithoutExtension +import kotlin.io.path.walk /** - * simple class for emulating RuleSetProvider to inject .yml rule configuration and mock this part of code + * Simple class for emulating [RuleSetProviderV2] to inject `.yml` rule configuration and mock this part of code. */ +@Suppress("serial") class DiktatRuleSetProvider4Test(private val ruleSupplier: (rulesConfigList: List) -> Rule, - rulesConfigList: List?) : RuleSetProvider { + rulesConfigList: List?) : RuleSetProviderV2( + id = DIKTAT_RULE_SET_ID, + about = NO_ABOUT, +) { private val rulesConfigList: List? = rulesConfigList ?: RulesConfigReader(javaClass.classLoader).readResource("diktat-analysis.yml") - @Suppress("OVERRIDE_DEPRECATION") - override fun get() = RuleSet( - DIKTAT_RULE_SET_ID, - ruleSupplier.invoke(rulesConfigList ?: emptyList()) - ) + override fun getRuleProviders(): Set = + setOf(RuleProvider { + ruleSupplier(rulesConfigList ?: emptyList()) + }) } class DiktatRuleSetProviderTest { + @OptIn(ExperimentalPathApi::class) @Suppress("UnsafeCallOnNullableType") @Test fun `check DiktatRuleSetProviderTest contain all rules`() { val path = "${System.getProperty("user.dir")}/src/main/kotlin/org/cqfn/diktat/ruleset/rules" - val filesName = File(path) + val fileNames = Path(path) .walk() - .filter { it.isFile } - .filter { file -> - /* - * Include only those files which contain `Rule` or `DiktatRule` - * descendants (any of the 1st 150 lines contains a superclass - * constructor call). - */ - val constructorCall = Regex(""":\s*(?:Diktat)?Rule\s*\(""") - file.bufferedReader().lineSequence().take(150) - .any { line -> - line.contains(constructorCall) - } - } - .map { it.nameWithoutExtension } - .filterNot { it in ignoreFile } - val rulesName = DiktatRuleSetProvider().get() + .filter(Path::isRegularFile) + .filterContentMatches(linesToRead = 150, Regex(""":\s*(?:Diktat)?Rule\s*\(""")) + .map(Path::nameWithoutExtension) + .filterNot { it in ignoredFileNames } + .toList() + val ruleNames = DiktatRuleSetProviderV2().getRuleProviders() .asSequence() + .map(RuleProvider::createNewRuleInstance) .onEachIndexed { index, rule -> if (index != 0) { Assertions.assertTrue( @@ -70,16 +67,22 @@ class DiktatRuleSetProviderTest { } } .map { it.delegatee() } - .map { it::class.simpleName!! } - .filterNot { it == "DummyWarning" } + .map { it::class.simpleName } + .filterNotNull() + .filterNot { it in ignoredRuleNames } .toList() - assertThat(rulesName.sorted()).containsExactlyElementsOf(filesName.sorted().toList()) + assertThat(fileNames).isNotEmpty + assertThat(ruleNames).isNotEmpty + assertThat(ruleNames.sorted()).containsExactlyElementsOf(fileNames.sorted()) } companion object { - private val ignoreFile = listOf( + private val ignoredFileNames = listOf( "DiktatRule", "OrderedRuleSet", ) + private val ignoredRuleNames = listOf( + "DummyWarning", + ) } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index 925c531ae8..3f07ffa9b1 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -1,7 +1,3 @@ -@file:Suppress( - "Deprecation" -) - package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt index d7341c1b7f..d1473f4cee 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt @@ -74,14 +74,13 @@ open class LintTestBase(private val ruleSupplier: (rulesConfigList: List = mutableListOf() - @Suppress("DEPRECATION") KtLint.lint( KtLint.ExperimentalParams( fileName = actualFileName, script = actualFileName.endsWith("kts"), text = code, - ruleSets = listOf(DiktatRuleSetProvider4Test(ruleSupplier, - rulesConfigList ?: this.rulesConfigList).get()), + ruleProviders = DiktatRuleSetProvider4Test(ruleSupplier, + rulesConfigList ?: this.rulesConfigList).getRuleProviders(), cb = { lintError, _ -> lintErrors += lintError }, ) ) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index 28af015790..d957b9544b 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -2,19 +2,16 @@ * Utility classes and methods for tests */ -@file:Suppress( - "Deprecation" -) - package org.cqfn.diktat.util import org.cqfn.diktat.ruleset.constants.EmitType import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSet +import com.pinterest.ktlint.core.RuleProvider import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail +import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.com.intellij.lang.ASTNode import java.util.concurrent.atomic.AtomicInteger @@ -41,7 +38,7 @@ internal fun T?.assertNotNull(lazyFailureMessage: () -> String = { "Expectin * @param applyToNode Function to be called on each AST node, should increment counter if assert is called */ @Suppress("TYPE_ALIAS") -internal fun applyToCode(code: String, +internal fun applyToCode(@Language("kotlin") code: String, expectedAsserts: Int, applyToNode: (node: ASTNode, counter: AtomicInteger) -> Unit ) { @@ -49,16 +46,16 @@ internal fun applyToCode(code: String, KtLint.lint( KtLint.ExperimentalParams( text = code, - ruleSets = listOf( - RuleSet("test", object : Rule("astnode-utils-test") { + ruleProviders = setOf(RuleProvider { + object : Rule("test:astnode-utils-test") { override fun beforeVisitChildNodes(node: ASTNode, autoCorrect: Boolean, emit: EmitType ) { applyToNode(node, counter) } - }) - ), + } + }), cb = { _, _ -> } ) ) diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index c2667014ab..7dbce960bc 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -3,10 +3,11 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.api.DiktatLogLevel import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.api.EditorConfigDefaults import com.pinterest.ktlint.core.api.EditorConfigOverride +import org.intellij.lang.annotations.Language import java.nio.file.Path import kotlin.io.path.absolutePathString @@ -18,7 +19,7 @@ import kotlin.io.path.absolutePathString */ class DiktatProcessCommand private constructor( val file: Path, - val fileContent: String, + @Language("kotlin") val fileContent: String, private val callback: DiktatCallback, private val isScript: Boolean, private val logLevel: DiktatLogLevel, @@ -37,12 +38,10 @@ class DiktatProcessCommand private constructor( KtLint.lint(ktLintParams()) } - @Suppress("DEPRECATION") private fun ktLintParams(): KtLint.ExperimentalParams = KtLint.ExperimentalParams( fileName = file.absolutePathString(), text = fileContent, - ruleSets = setOf(DiktatRuleSetProvider().get()), - ruleProviders = emptySet(), + ruleProviders = DiktatRuleSetProviderV2().getRuleProviders(), userData = emptyMap(), cb = callback.unwrap(), script = isScript, @@ -64,7 +63,7 @@ class DiktatProcessCommand private constructor( */ data class Builder( var file: Path? = null, - var fileContent: String? = null, + @Language("kotlin") var fileContent: String? = null, var callback: DiktatCallback? = null, var isScript: Boolean? = null, var logLevel: DiktatLogLevel = DiktatLogLevel.INFO, @@ -79,7 +78,7 @@ class DiktatProcessCommand private constructor( * @param fileContent * @return updated builder */ - fun fileContent(fileContent: String) = apply { this.fileContent = fileContent } + fun fileContent(@Language("kotlin") fileContent: String) = apply { this.fileContent = fileContent } /** * @param callback diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt index 37c753bf73..d85eb1dbfe 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt @@ -3,9 +3,9 @@ package org.cqfn.diktat.ruleset.smoke import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import org.cqfn.diktat.test.framework.util.deleteIfExistsRecursively import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently +import org.cqfn.diktat.test.framework.util.isSameJavaHomeAs +import org.cqfn.diktat.test.framework.util.prependPath import org.cqfn.diktat.test.framework.util.retry -import org.cqfn.diktat.util.isSameJavaHomeAs -import org.cqfn.diktat.util.prependPath import com.pinterest.ktlint.core.LintError import mu.KotlinLogging diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt index 46cf775a42..427b1797f3 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -1,6 +1,6 @@ package org.cqfn.diktat.ruleset.smoke -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import org.cqfn.diktat.ruleset.utils.format import org.cqfn.diktat.test.framework.processing.TestComparatorUnit import com.pinterest.ktlint.core.LintError @@ -10,7 +10,7 @@ import java.nio.file.Path import kotlin.io.path.absolutePathString /** - * Test for [DiktatRuleSetProvider] in autocorrect mode as a whole. All rules are applied to a file. + * Test for [DiktatRuleSetProviderV2] in autocorrect mode as a whole. All rules are applied to a file. * Note: ktlint uses initial text from a file to calculate line and column from offset. Because of that line/col of unfixed errors * may change after some changes to text or other rules. */ @@ -43,7 +43,7 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { resourceFilePath = RESOURCE_FILE_PATH, function = { expectedText, testFilePath -> format( - ruleSetProviderRef = { DiktatRuleSetProvider(config.absolutePathString()) }, + ruleSetProviderRef = { DiktatRuleSetProviderV2(config.absolutePathString()) }, text = expectedText, fileName = testFilePath, cb = { lintError, _ -> unfixedLintErrors.add(lintError) }, diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt deleted file mode 100644 index 8b69310762..0000000000 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Utility classes and methods for tests - */ - -package org.cqfn.diktat.util - -import java.io.File -import java.nio.file.NoSuchFileException -import java.nio.file.Path -import kotlin.io.path.isDirectory -import kotlin.io.path.isSameFileAs - -/** - * @receiver the 1st operand. - * @param other the 2nd operand. - * @return `true` if, and only if, the two paths locate the same `JAVA_HOME`. - */ -internal fun Path.isSameJavaHomeAs(other: Path): Boolean = - isDirectory() && - (isSameFileAsSafe(other) || - resolve("jre").isSameFileAsSafe(other) || - other.resolve("jre").isSameFileAsSafe(this)) - -/** - * The same as [Path.isSameFileAs], but doesn't throw any [NoSuchFileException] - * if either of the operands doesn't exist. - * - * @receiver the 1st operand. - * @param other the 2nd operand. - * @return `true` if, and only if, the two paths locate the same file. - * @see Path.isSameFileAs - */ -internal fun Path.isSameFileAsSafe(other: Path): Boolean = - try { - isSameFileAs(other) - } catch (_: NoSuchFileException) { - false - } - -/** - * Prepends the `PATH` of this process builder with [pathEntry]. - * - * @param pathEntry the entry to be prepended to the `PATH`. - */ -internal fun ProcessBuilder.prependPath(pathEntry: Path) { - require(pathEntry.isDirectory()) { - "$pathEntry is not a directory" - } - - val environment = environment() - - val defaultPathKey = "PATH" - val defaultWindowsPathKey = "Path" - - val pathKey = when { - /*- - * Keys of the Windows environment are case-insensitive ("PATH" == "Path"). - * Keys of the Java interface to the environment are not ("PATH" != "Path"). - * This is an attempt to work around the inconsistency. - */ - System.getProperty("os.name").startsWith("Windows") -> environment.keys.firstOrNull { key -> - key.equals(defaultPathKey, ignoreCase = true) - } ?: defaultWindowsPathKey - - else -> defaultPathKey - } - - val pathSeparator = File.pathSeparatorChar - val oldPath = environment[pathKey] - - val newPath = when { - oldPath.isNullOrEmpty() -> pathEntry.toString() - else -> "$pathEntry$pathSeparator$oldPath" - } - - environment[pathKey] = newPath -} diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt index 564297cf38..8d65e2a70a 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt @@ -8,9 +8,11 @@ import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import mu.KotlinLogging +import java.io.File import java.io.IOException import java.nio.file.FileVisitResult import java.nio.file.FileVisitResult.CONTINUE +import java.nio.file.Files import java.nio.file.Files.walkFileTree import java.nio.file.NoSuchFileException import java.nio.file.Path @@ -18,8 +20,11 @@ import java.nio.file.SimpleFileVisitor import java.nio.file.attribute.BasicFileAttributes import kotlin.io.path.absolute +import kotlin.io.path.bufferedReader import kotlin.io.path.deleteExisting import kotlin.io.path.deleteIfExists +import kotlin.io.path.isDirectory +import kotlin.io.path.isSameFileAs @Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") private val log = KotlinLogging.loggerWithKtlintConfig {} @@ -91,6 +96,130 @@ fun Path.deleteIfExistsRecursively(): Boolean = false } +/** + * @receiver the 1st operand. + * @param other the 2nd operand. + * @return `true` if, and only if, the two paths locate the same `JAVA_HOME`. + */ +fun Path.isSameJavaHomeAs(other: Path): Boolean = + isDirectory() && + (isSameFileAsSafe(other) || + resolve("jre").isSameFileAsSafe(other) || + other.resolve("jre").isSameFileAsSafe(this)) + +/** + * The same as [Path.isSameFileAs], but doesn't throw any [NoSuchFileException] + * if either of the operands doesn't exist. + * + * @receiver the 1st operand. + * @param other the 2nd operand. + * @return `true` if, and only if, the two paths locate the same file. + * @see Path.isSameFileAs + */ +fun Path.isSameFileAsSafe(other: Path): Boolean = + try { + isSameFileAs(other) + } catch (_: NoSuchFileException) { + false + } + +/** + * Requests that this file or directory be deleted when the JVM terminates. + * + * Does nothing if this [Path] is not associated with the default provider. + * + * @receiver a regular file or a directory. + * @return this [Path]. + */ +fun Path.tryToDeleteOnExit(): Path { + try { + toFile().deleteOnExit() + } catch (_: UnsupportedOperationException) { + /* + * Ignore. + */ + } + + return this +} + +/** + * Resets any permissions which might otherwise prevent from reading or writing + * this file or directory, or traversing this directory. + * + * @receiver a regular file or a directory. + */ +fun Path.resetPermissions() { + toFile().apply { + setReadable(true) + setWritable(true) + + if (isDirectory) { + setExecutable(true) + } + } +} + +/** + * Returns a sequence containing only files whose content (the first + * [linesToRead] lines) matches [lineRegex]. + * + * The operation is _intermediate_ and _stateless_. + * + * @receiver a sequence of regular files. + * @param linesToRead the number of lines to read (at most). + * @param lineRegex the regular expression to be applied to each line until a + * match is found (i.e. the line is found which _contains_ [lineRegex]). + * @return the filtered sequence. + */ +fun Sequence.filterContentMatches(linesToRead: Int, lineRegex: Regex): Sequence = + filter { file -> + file.bufferedReader().useLines { lines -> + lines.take(linesToRead).any { line -> + line.contains(lineRegex) + } + } + } + +/** + * Prepends the `PATH` of this process builder with [pathEntry]. + * + * @param pathEntry the entry to be prepended to the `PATH`. + */ +fun ProcessBuilder.prependPath(pathEntry: Path) { + require(pathEntry.isDirectory()) { + "$pathEntry is not a directory" + } + + val environment = environment() + + val defaultPathKey = "PATH" + val defaultWindowsPathKey = "Path" + + val pathKey = when { + /*- + * Keys of the Windows environment are case-insensitive ("PATH" == "Path"). + * Keys of the Java interface to the environment are not ("PATH" != "Path"). + * This is an attempt to work around the inconsistency. + */ + System.getProperty("os.name").startsWith("Windows") -> environment.keys.firstOrNull { key -> + key.equals(defaultPathKey, ignoreCase = true) + } ?: defaultWindowsPathKey + + else -> defaultPathKey + } + + val pathSeparator = File.pathSeparatorChar + val oldPath = environment[pathKey] + + val newPath = when { + oldPath.isNullOrEmpty() -> pathEntry.toString() + else -> "$pathEntry$pathSeparator$oldPath" + } + + environment[pathKey] = newPath +} + /** * Retries the execution of the [block]. * diff --git a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt index 483d8973d8..8ba319eb8e 100644 --- a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt +++ b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt @@ -15,7 +15,6 @@ import java.io.PrintWriter "MagicNumber", "ComplexMethod", "NestedBlockDepth", - "WRONG_INDENTATION", "TOO_LONG_FUNCTION") fun generateCodeStyle(guideDir: File, wpDir: File) { val file = File(guideDir, "diktat-coding-convention.md") @@ -207,7 +206,6 @@ private fun handleHyperlinks(line: String): String { return correctedString } -@Suppress("WRONG_INDENTATION") private fun findBoldOrItalicText(regex: Regex, line: String, type: FindType): String { @@ -228,4 +226,4 @@ private fun findBoldOrItalicText(regex: Regex, return correctedLine } -private fun String.getFirstNumber() = trimStart().takeWhile { it.isDigit() || it == '.' } \ No newline at end of file +private fun String.getFirstNumber() = trimStart().takeWhile { it.isDigit() || it == '.' } From 9a9e768f668926cc883cb4ee5ccffaab53f5b3bf Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 6 Dec 2022 12:16:21 +0300 Subject: [PATCH 04/33] Add main for diktat.jar (#1566) --- diktat-common/pom.xml | 34 ---- diktat-maven-plugin/pom.xml | 23 --- .../diktat/plugin/maven/DiktatBaseMojo.kt | 15 +- .../cqfn/diktat/plugin/maven/DiktatMojo.kt | 3 +- .../diktat/plugin/maven/DiktatBaseMojoTest.kt | 11 +- diktat-rules/pom.xml | 23 --- .../cqfn/diktat/ruleset/utils/FileUtils.kt | 23 ++- diktat-ruleset/pom.xml | 124 ++++++------- .../main/kotlin/org/cqfn/diktat/DiktatMain.kt | 85 +++++++++ .../org/cqfn/diktat/DiktatProcessCommand.kt | 70 ++++---- .../org/cqfn/diktat/api/DiktatLogLevel.kt | 10 -- .../kotlin/org/cqfn/diktat/api/DiktatMode.kt | 16 ++ .../org/cqfn/diktat/cli/DiktatProperties.kt | 165 ++++++++++++++++++ .../org/cqfn/diktat/ktlint/ReporterUtil.kt | 133 ++++++++++++++ .../kotlin/org/cqfn/diktat/util/FileUtils.kt | 36 ++++ .../src/main/resources/META-INF/version | 2 + diktat-test-framework/pom.xml | 39 ----- pom.xml | 73 +++++++- 18 files changed, 619 insertions(+), 266 deletions(-) create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt create mode 100644 diktat-ruleset/src/main/resources/META-INF/version diff --git a/diktat-common/pom.xml b/diktat-common/pom.xml index fdf999e628..686ac1d5d9 100644 --- a/diktat-common/pom.xml +++ b/diktat-common/pom.xml @@ -57,40 +57,6 @@ org.jetbrains.kotlin kotlin-maven-plugin - - - compile - process-sources - - compile - - - - test-compile - process-test-sources - - test-compile - - - - src/test/kotlin - src/test/resources - - - - - - - kotlinx-serialization - - - - - org.jetbrains.kotlin - kotlin-maven-serialization - ${kotlin.version} - - diff --git a/diktat-maven-plugin/pom.xml b/diktat-maven-plugin/pom.xml index e1244be738..1a9231b0b5 100644 --- a/diktat-maven-plugin/pom.xml +++ b/diktat-maven-plugin/pom.xml @@ -49,7 +49,6 @@ org.cqfn.diktat diktat - ${project.version} com.pinterest.ktlint @@ -116,28 +115,6 @@ org.jetbrains.kotlin kotlin-maven-plugin - - - compile - process-sources - - compile - - - - test-compile - process-test-sources - - test-compile - - - - - src/test/kotlin - - - - org.apache.maven.plugins diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 3c29da0ac4..f0ae9ccee1 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -5,8 +5,8 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand -import org.cqfn.diktat.api.DiktatLogLevel import org.cqfn.diktat.ktlint.unwrap +import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Reporter @@ -36,12 +36,6 @@ import java.io.PrintStream * Base [Mojo] for checking and fixing code using diktat */ abstract class DiktatBaseMojo : AbstractMojo() { - /** - * Flag that indicates whether to turn debug logging on - */ - @Parameter(property = "diktat.debug") - var debug = false - /** * Property that will be used if you need to publish the report to GitHub */ @@ -204,7 +198,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { directory .walk() .filter { file -> - file.isDirectory || file.extension.let { it == "kt" || it == "kts" } + file.isDirectory || file.toPath().isKotlinCodeOrScript() } .filter { it.isFile } .filterNot { file -> file in excludedFiles || excludedDirs.any { file.startsWith(it) } } @@ -240,8 +234,6 @@ abstract class DiktatBaseMojo : AbstractMojo() { ) { val command = DiktatProcessCommand.Builder() .file(file.toPath()) - .fileContent(file.readText(Charsets.UTF_8)) - .isScript(file.extension.equals("kts", ignoreCase = true)) .callback { error, isCorrected -> val ktLintError = error.unwrap() if (!baselineErrors.containsLintError(ktLintError)) { @@ -249,9 +241,6 @@ abstract class DiktatBaseMojo : AbstractMojo() { lintErrors.add(ktLintError) } } - .logLevel( - if (debug) DiktatLogLevel.DEBUG else DiktatLogLevel.INFO - ) .build() runAction(command) } diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt index bc230de2b6..2f716a322f 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt @@ -9,6 +9,7 @@ import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import org.apache.maven.plugins.annotations.Mojo import kotlin.io.path.absolutePathString +import kotlin.io.path.readText import kotlin.io.path.writeText /** @@ -37,7 +38,7 @@ class DiktatFixMojo : DiktatBaseMojo() { */ override fun runAction(command: DiktatProcessCommand) { val fileName = command.file.absolutePathString() - val fileContent = command.fileContent + val fileContent = command.file.readText(Charsets.UTF_8) val formattedText = command.fix() if (fileContent != formattedText) { log.info("Original and formatted content differ, writing to $fileName...") diff --git a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt index 5f76547344..0d2ef274ab 100644 --- a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt +++ b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojoTest.kt @@ -44,11 +44,11 @@ class DiktatBaseMojoTest { xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - + org.cqfn.diktat diktat-test 1.0.0-SNAPSHOT - + @@ -70,7 +70,6 @@ class DiktatBaseMojoTest { val mavenProject = projectBuilder.build(pom, buildingRequest).project val diktatCheckMojo = mojoRule.lookupConfiguredMojo(mavenProject, "check") as DiktatCheckMojo - Assertions.assertFalse(diktatCheckMojo.debug) Assertions.assertEquals("diktat-analysis.yml", diktatCheckMojo.diktatConfigFile) Assertions.assertIterableEquals(listOf(pom.parentFile.toPath() / "src"), diktatCheckMojo.inputs.map { Path(it) }) Assertions.assertTrue(diktatCheckMojo.excludes.isEmpty()) @@ -85,18 +84,17 @@ class DiktatBaseMojoTest { xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - + org.cqfn.diktat diktat-test 1.0.0-SNAPSHOT - + org.cqfn.diktat diktat-maven-plugin - true my-diktat-config.yml ${'$'}{project.basedir}/src/main/kotlin @@ -122,7 +120,6 @@ class DiktatBaseMojoTest { val mavenProject = projectBuilder.build(pom, buildingRequest).project val diktatCheckMojo = mojoRule.lookupConfiguredMojo(mavenProject, "check") as DiktatCheckMojo - Assertions.assertTrue(diktatCheckMojo.debug) Assertions.assertEquals("my-diktat-config.yml", diktatCheckMojo.diktatConfigFile) Assertions.assertIterableEquals( listOf(pom.parentFile.toPath() / "src/main/kotlin", pom.parentFile.toPath() / "src/test/kotlin"), diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml index f7b4cf55b7..37e3b944d2 100644 --- a/diktat-rules/pom.xml +++ b/diktat-rules/pom.xml @@ -96,29 +96,6 @@ - - compile - compile - - compile - - - - test-compile - process-test-sources - - test-compile - - - - - src/main/kotlin - src/test/kotlin - ${project.basedir}/src/main/kotlin - ${project.basedir}/src/test/kotlin - - - diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/FileUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/FileUtils.kt index 0e349bbeb6..2eaa177b93 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/FileUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/FileUtils.kt @@ -4,7 +4,12 @@ package org.cqfn.diktat.ruleset.utils +import java.nio.file.Path +import kotlin.io.path.extension + internal const val SRC_DIRECTORY_NAME = "src" +private const val KOTLIN_EXTENSION = "kt" +private const val KOTLIN_SCRIPT_EXTENSION = KOTLIN_EXTENSION + "s" /** * Splits [this] string by file path separator. @@ -17,11 +22,25 @@ fun String.splitPathToDirs(): List = .split("/") /** - * Checks if [this] String is a name of a kotlin script file by checking whether file extension equals 'kts' + * Checks if [this] [String] is a name of a kotlin script file by checking whether file extension equals 'kts' + * + * @return true if this is a kotlin script file name, false otherwise + */ +fun String.isKotlinScript() = endsWith(".$KOTLIN_SCRIPT_EXTENSION") + +/** + * Check if [this] [Path] is a kotlin script by checking whether an extension equals to 'kts' * * @return true if this is a kotlin script file name, false otherwise */ -fun String.isKotlinScript() = endsWith(".kts") +fun Path.isKotlinScript() = this.extension.lowercase() == KOTLIN_SCRIPT_EXTENSION + +/** + * Check if [this] [Path] is a kotlin code or script by checking whether an extension equals to `kt` or 'kts' + * + * @return true if this is a kotlin code or script file name, false otherwise + */ +fun Path.isKotlinCodeOrScript() = this.extension.lowercase() in setOf(KOTLIN_EXTENSION, KOTLIN_SCRIPT_EXTENSION) /** * Checks if [this] String is a name of a gradle kotlin script file by checking whether file extension equals 'gradle.kts' diff --git a/diktat-ruleset/pom.xml b/diktat-ruleset/pom.xml index de6d80c22e..6b169771fb 100644 --- a/diktat-ruleset/pom.xml +++ b/diktat-ruleset/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diktat - This module builds jar that can be used to run diktat using ktlint -R via command line + This module builds jar that can be used to run diktat using java -jar via command line org.cqfn.diktat @@ -17,58 +17,47 @@ org.cqfn.diktat diktat-rules ${project.version} - - - - - org.jetbrains.kotlin - kotlin-stdlib-common - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - - - org.jetbrains.kotlin - kotlin-stdlib - - - org.jetbrains.kotlin - kotlin-compiler-embeddable - - - - org.cqfn.diktat - diktat-test-framework - ${project.version} - test + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + - org.jetbrains.kotlin - kotlin-stdlib-common - provided + com.pinterest.ktlint + ktlint-reporter-plain - org.jetbrains.kotlin - kotlin-stdlib-jdk7 - provided + com.pinterest.ktlint + ktlint-reporter-json - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - provided + com.pinterest.ktlint + ktlint-reporter-sarif - org.jetbrains.kotlin - kotlin-stdlib - provided + com.pinterest.ktlint + ktlint-reporter-checkstyle - org.jetbrains.kotlin - kotlin-compiler-embeddable - provided + com.pinterest.ktlint + ktlint-reporter-html + + + + org.jetbrains.kotlinx + kotlinx-cli-jvm + + + + org.cqfn.diktat + diktat-test-framework + ${project.version} + test org.junit.jupiter @@ -93,43 +82,29 @@ + ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin + + + src/main/resources/META-INF + + version + + META-INF/diktat + true + + + ${project.basedir}/.. + + LICENSE + + META-INF/diktat + + org.jetbrains.kotlin kotlin-maven-plugin - - - compile - process-sources - - compile - - - - src/main/java - src/main/kotlin - src/main/resources - - - - - test-compile - process-test-sources - - test-compile - - - - - src/main/kotlin - src/test/kotlin - ${project.basedir}/src/main/kotlin - ${project.basedir}/src/test/kotlin - - - - @@ -142,6 +117,9 @@ diktat-${project.version} false + + org.cqfn.diktat.DiktatMainKt + true diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt new file mode 100644 index 0000000000..506d257dc7 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt @@ -0,0 +1,85 @@ +/** + * The file contains main method + */ + +package org.cqfn.diktat + +import org.cqfn.diktat.api.DiktatError +import org.cqfn.diktat.api.DiktatMode +import org.cqfn.diktat.cli.DiktatProperties +import org.cqfn.diktat.common.utils.loggerWithKtlintConfig +import org.cqfn.diktat.ktlint.unwrap +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactory +import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript +import org.cqfn.diktat.util.tryToPathIfExists +import org.cqfn.diktat.util.walkByGlob +import mu.KotlinLogging +import java.nio.file.Paths +import kotlin.io.path.absolutePathString +import kotlin.io.path.writeText + +@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") +private val log = KotlinLogging.loggerWithKtlintConfig {} + +typealias DiktatErrorWithCorrectionInfo = Pair + +@Suppress( + "LongMethod", + "TOO_LONG_FUNCTION" +) +fun main(args: Array) { + val properties = DiktatProperties.parse(args) + properties.configureLogger() + + log.debug { + "Loading diktatRuleSet using config ${properties.config}" + } + val diktatRuleSetFactory = DiktatRuleSetFactory(properties.config) + val reporter = properties.reporter() + reporter.beforeAll() + + log.debug { + "Resolving files by patterns: ${properties.patterns}" + } + val currentFolder = Paths.get(".") + properties.patterns + .asSequence() + .flatMap { pattern -> + pattern.tryToPathIfExists()?.let { sequenceOf(it) } + ?: currentFolder.walkByGlob(pattern) + } + .filter { file -> file.isKotlinCodeOrScript() } + .distinct() + .map { it.normalize() } + .map { file -> + log.debug { + "Start processing the file: $file" + } + val result: MutableList = mutableListOf() + DiktatProcessCommand.Builder() + .file(file) + .diktatRuleSetFactory(diktatRuleSetFactory) + .callback { error, isCorrected -> + result.add(error to isCorrected) + } + .build() + .let { command -> + when (properties.mode) { + DiktatMode.CHECK -> command.check() + DiktatMode.FIX -> { + val formattedFileContent = command.fix() + file.writeText(formattedFileContent, Charsets.UTF_8) + } + } + } + file to result + } + .forEach { (file, result) -> + reporter.before(file.absolutePathString()) + result.forEach { (error, isCorrected) -> + reporter.onLintError(file.absolutePathString(), error.unwrap(), isCorrected) + } + reporter.after(file.absolutePathString()) + } + reporter.afterAll() +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index 7dbce960bc..3998859905 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -1,29 +1,34 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.api.DiktatLogLevel +import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF import org.cqfn.diktat.ktlint.unwrap +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactory import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 +import org.cqfn.diktat.ruleset.utils.isKotlinScript import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.api.EditorConfigDefaults import com.pinterest.ktlint.core.api.EditorConfigOverride -import org.intellij.lang.annotations.Language +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.nio.file.Path import kotlin.io.path.absolutePathString +import kotlin.io.path.readText /** * Command to run `diktat` * * @property file - * @property fileContent */ class DiktatProcessCommand private constructor( val file: Path, - @Language("kotlin") val fileContent: String, + private val diktatRuleSetFactory: DiktatRuleSetFactory, private val callback: DiktatCallback, - private val isScript: Boolean, - private val logLevel: DiktatLogLevel, ) { + private val isDebug: Boolean by lazy { + LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).isDebugEnabled + } + /** * Run `diktat fix` using parameters from current command * @@ -40,13 +45,14 @@ class DiktatProcessCommand private constructor( private fun ktLintParams(): KtLint.ExperimentalParams = KtLint.ExperimentalParams( fileName = file.absolutePathString(), - text = fileContent, - ruleProviders = DiktatRuleSetProviderV2().getRuleProviders(), + text = file.readText(Charsets.UTF_8), + ruleSets = emptySet(), + ruleProviders = DiktatRuleSetProviderV2(diktatRuleSetFactory).getRuleProviders(), userData = emptyMap(), cb = callback.unwrap(), - script = isScript, + script = file.isKotlinScript(), editorConfigPath = null, - debug = logLevel == DiktatLogLevel.DEBUG, + debug = isDebug, editorConfigDefaults = EditorConfigDefaults.emptyEditorConfigDefaults, editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride, isInvokedFromCli = false @@ -56,17 +62,15 @@ class DiktatProcessCommand private constructor( * Builder for [DiktatProcessCommand] * * @property file - * @property fileContent + * @property diktatRuleSetFactory + * @property config * @property callback - * @property isScript - * @property logLevel */ data class Builder( var file: Path? = null, - @Language("kotlin") var fileContent: String? = null, + var diktatRuleSetFactory: DiktatRuleSetFactory? = null, + var config: String? = null, var callback: DiktatCallback? = null, - var isScript: Boolean? = null, - var logLevel: DiktatLogLevel = DiktatLogLevel.INFO, ) { /** * @param file @@ -75,28 +79,30 @@ class DiktatProcessCommand private constructor( fun file(file: Path) = apply { this.file = file } /** - * @param fileContent - * @return updated builder - */ - fun fileContent(@Language("kotlin") fileContent: String) = apply { this.fileContent = fileContent } - - /** - * @param callback + * @param diktatRuleSetFactory * @return updated builder */ - fun callback(callback: DiktatCallback) = apply { this.callback = callback } + fun diktatRuleSetFactory(diktatRuleSetFactory: DiktatRuleSetFactory) = require(config == null) { + "diktatRuleSetFactory is set already via config" + }.let { + apply { this.diktatRuleSetFactory = diktatRuleSetFactory } + } /** - * @param isScript + * @param config * @return updated builder */ - fun isScript(isScript: Boolean) = apply { this.isScript = isScript } + fun config(config: String) = require(diktatRuleSetFactory == null) { + "diktatRuleSetFactory is already provided directly" + }.let { + apply { this.config = config } + } /** - * @param logLevel + * @param callback * @return updated builder */ - fun logLevel(logLevel: DiktatLogLevel) = apply { this.logLevel = logLevel } + fun callback(callback: DiktatCallback) = apply { this.callback = callback } /** * @return built [DiktatProcessCommand] @@ -105,16 +111,10 @@ class DiktatProcessCommand private constructor( requireNotNull(file) { "file is required" }, - requireNotNull(fileContent) { - "fileContent is required" - }, + diktatRuleSetFactory ?: DiktatRuleSetFactory(config ?: DIKTAT_ANALYSIS_CONF), requireNotNull(callback) { "callback is required" }, - requireNotNull(isScript) { - "isScript is required" - }, - logLevel, ) } } diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt deleted file mode 100644 index 6e6e18641d..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatLogLevel.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.cqfn.diktat.api - -/** - * Log level of `diktat processing` - */ -enum class DiktatLogLevel { - DEBUG, - INFO, - ; -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt new file mode 100644 index 0000000000..aabe4ea9c5 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt @@ -0,0 +1,16 @@ +package org.cqfn.diktat.api + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Mode of `diktat` + */ +@Serializable +enum class DiktatMode { + @SerialName("check") + CHECK, + @SerialName("fix") + FIX, + ; +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt new file mode 100644 index 0000000000..a03c0960c4 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt @@ -0,0 +1,165 @@ +package org.cqfn.diktat.cli + +import org.cqfn.diktat.api.DiktatMode +import org.cqfn.diktat.common.config.rules.DIKTAT +import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF +import org.cqfn.diktat.ktlint.buildReporter +import org.cqfn.diktat.ktlint.colorName +import org.cqfn.diktat.ktlint.reporterProviderId +import com.pinterest.ktlint.core.Reporter +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.core.LoggerContext +import org.slf4j.event.Level +import kotlin.system.exitProcess +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default +import kotlinx.cli.vararg + +/** + * @property config path to `diktat-analysis.yml` + * @property mode mode of `diktat` + * @property reporterProviderId + * @property output + * @property patterns + */ +data class DiktatProperties( + val config: String, + val mode: DiktatMode, + val reporterProviderId: String, + val output: String?, + private val groupByFileInPlain: Boolean, + private val colorNameInPlain: String?, + private val logLevel: Level, + val patterns: List, +) { + /** + * @return a configured [Reporter] + */ + fun reporter(): Reporter = buildReporter( + reporterProviderId, output, colorNameInPlain, groupByFileInPlain, mode + ) + + /** + * Configure logger level using [logLevel] + */ + fun configureLogger() { + // set log level + LogManager.getContext(false) + .let { it as LoggerContext } + .also { ctx -> + ctx.configuration.rootLogger.level = when (logLevel) { + Level.ERROR -> org.apache.logging.log4j.Level.ERROR + Level.WARN -> org.apache.logging.log4j.Level.WARN + Level.INFO -> org.apache.logging.log4j.Level.INFO + Level.DEBUG -> org.apache.logging.log4j.Level.DEBUG + Level.TRACE -> org.apache.logging.log4j.Level.TRACE + } + } + .updateLoggers() + } + + companion object { + /** + * @param args cli arguments + * @return parsed [DiktatProperties] + */ + @Suppress( + "LongMethod", + "TOO_LONG_FUNCTION" + ) + fun parse(args: Array): DiktatProperties { + val parser = ArgParser(DIKTAT) + val config: String by parser.option( + type = ArgType.String, + fullName = "config", + shortName = "c", + description = "Specify the location of the YAML configuration file. By default, $DIKTAT_ANALYSIS_CONF in the current directory is used.", + ).default(DIKTAT_ANALYSIS_CONF) + val mode: DiktatMode by parser.option( + type = ArgType.Choice(), + fullName = "mode", + shortName = "m", + description = "Mode of `diktat` controls that `diktat` fixes or only finds any deviations from the code style." + ).default(DiktatMode.CHECK) + val reporterProviderId: String by parser.reporterProviderId() + val output: String? by parser.option( + type = ArgType.String, + fullName = "output", + shortName = "o", + description = "Redirect the reporter output to a file.", + ) + val groupByFileInPlain: Boolean by parser.option( + type = ArgType.Boolean, + fullName = "plain-group-by-file", + shortName = null, + description = "A flag for plain reporter" + ).default(false) + val colorName: String? by parser.colorName() + val logLevel: Level by parser.option( + type = ArgType.Choice(), + fullName = "log-level", + shortName = "l", + description = "Enable the output with specific level", + ).default(Level.INFO) + val patterns: List by parser.argument( + type = ArgType.String, + description = "A list of files to process by diktat" + ).vararg() + + parser.addOptionAndShowResourceWithExit( + fullName = "version", + shortName = "V", + description = "Output version information and exit.", + args = args, + resourceName = "META-INF/diktat/version" + ) + parser.addOptionAndShowResourceWithExit( + fullName = "license", + shortName = null, + description = "Display the license and exit.", + args = args, + resourceName = "META-INF/diktat/LICENSE", + ) + + parser.parse(args) + return DiktatProperties( + config = config, + mode = mode, + reporterProviderId = reporterProviderId, + output = output, + groupByFileInPlain = groupByFileInPlain, + colorNameInPlain = colorName, + logLevel = logLevel, + patterns = patterns, + ) + } + + private fun ArgParser.addOptionAndShowResourceWithExit( + fullName: String, + shortName: String?, + description: String, + args: Array, + resourceName: String, + ) { + // add here to print in help + option( + type = ArgType.Boolean, + fullName = fullName, + shortName = shortName, + description = description + ) + if (args.contains("--$fullName") || shortName?.let { args.contains("-$it") } == true) { + @Suppress("DEBUG_PRINT", "ForbiddenMethodCall") + print(readFromResource(resourceName)) + exitProcess(0) + } + } + + private fun readFromResource(resourceName: String): String = DiktatProperties::class.java + .classLoader + .getResource(resourceName) + ?.readText() + ?: error("Resource $resourceName not found") + } +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt new file mode 100644 index 0000000000..6cc546638b --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt @@ -0,0 +1,133 @@ +/** + * File contains util methods to create ktlint's [Reporter] + */ + +package org.cqfn.diktat.ktlint + +import org.cqfn.diktat.api.DiktatMode +import com.pinterest.ktlint.core.Reporter +import com.pinterest.ktlint.core.ReporterProvider +import com.pinterest.ktlint.reporter.checkstyle.CheckStyleReporterProvider +import com.pinterest.ktlint.reporter.html.HtmlReporterProvider +import com.pinterest.ktlint.reporter.json.JsonReporterProvider +import com.pinterest.ktlint.reporter.plain.PlainReporterProvider +import com.pinterest.ktlint.reporter.sarif.SarifReporterProvider +import java.io.PrintStream +import java.nio.file.Paths +import kotlin.io.path.createDirectories +import kotlin.io.path.outputStream +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default + +private const val DEFAULT_COLOR_NAME = "DARK_GRAY" + +private val plainReporterProvider = PlainReporterProvider() + +private val reporterProviders = setOf( + plainReporterProvider, + JsonReporterProvider(), + SarifReporterProvider(), + CheckStyleReporterProvider(), + HtmlReporterProvider(), +) + .associateBy { it.id } + +// supported color names in KtLint +private val colorNames = listOf( + "BLACK", + "RED", + "GREEN", + "YELLOW", + "BLUE", + "MAGENTA", + "CYAN", + "LIGHT_GRAY", + DEFAULT_COLOR_NAME, + "LIGHT_RED", + "LIGHT_GREEN", + "LIGHT_YELLOW", + "LIGHT_BLUE", + "LIGHT_MAGENTA", + "LIGHT_CYAN", + "WHITE", +) + +/** + * @return a single [ReporterProvider] as parsed cli arg + */ +internal fun ArgParser.reporterProviderId() = option( + type = ArgType.Choice( + choices = reporterProviders.keys.toList(), + toVariant = { it }, + variantToString = { it }, + ), + fullName = "reporter", + shortName = "r", + description = "The reporter to use" +) + .default(plainReporterProvider.id) + +/** + * @return a single and optional color name as parsed cli args + */ +internal fun ArgParser.colorName() = this.option( + type = ArgType.Choice( + choices = colorNames, + toVariant = { it }, + variantToString = { it }, + ), + fullName = "plain-color", + shortName = null, + description = "Colorize the output.", +) + +/** + * @return true if receiver is [PlainReporterProvider] + */ +internal fun ReporterProvider<*>.isPlain(): Boolean = id == plainReporterProvider.id + +/** + * @param reporterProviderId + * @param output + * @param colorNameInPlain + * @param groupByFileInPlain + * @param mode + * @return a configured [Reporter] + */ +internal fun buildReporter( + reporterProviderId: String, + output: String?, + colorNameInPlain: String?, + groupByFileInPlain: Boolean, + mode: DiktatMode, +): Reporter { + val reporterProvider = reporterProviders.getValue(reporterProviderId) + return reporterProvider.get( + out = output + ?.let { Paths.get(it) } + ?.also { it.parent.createDirectories() } + ?.outputStream() + ?.let { PrintStream(it) } + ?: System.out, + opt = buildMap { + colorNameInPlain?.let { + require(reporterProvider.isPlain()) { + "colorization is applicable only for plain reporter" + } + put("color", true) + put("color_name", it) + } ?: run { + put("color", false) + put("color_name", DEFAULT_COLOR_NAME) + } + put("format", (mode == DiktatMode.FIX)) + if (groupByFileInPlain) { + require(reporterProvider.isPlain()) { + "groupByFile is applicable only for plain reporter" + } + put("group_by_file", true) + } + }.mapValues { it.value.toString() }, + ) +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt new file mode 100644 index 0000000000..22f5027272 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt @@ -0,0 +1,36 @@ +/** + * This class contains util methods to operate with java.nio.file.Path + */ + +package org.cqfn.diktat.util + +import java.nio.file.InvalidPathException +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.PathWalkOption +import kotlin.io.path.exists +import kotlin.io.path.walk + +/** + * Create a matcher and return a filter that uses it. + * + * @param glob glob pattern to filter files + * @return a sequence of files which matches to [glob] + */ +@OptIn(ExperimentalPathApi::class) +fun Path.walkByGlob(glob: String): Sequence = + fileSystem.getPathMatcher("glob:$glob") + .let { matcher -> + this.walk(PathWalkOption.INCLUDE_DIRECTORIES) + .filter { matcher.matches(it) } + } + +/** + * @return path or null if path is invalid or doesn't exist + */ +fun String.tryToPathIfExists(): Path? = try { + Paths.get(this).takeIf { it.exists() } +} catch (e: InvalidPathException) { + null +} diff --git a/diktat-ruleset/src/main/resources/META-INF/version b/diktat-ruleset/src/main/resources/META-INF/version new file mode 100644 index 0000000000..abae2e47cf --- /dev/null +++ b/diktat-ruleset/src/main/resources/META-INF/version @@ -0,0 +1,2 @@ +diktat: ${project.version} +ktlint: ${ktlint.version} diff --git a/diktat-test-framework/pom.xml b/diktat-test-framework/pom.xml index bcb567e5f6..e68a7f8a9a 100644 --- a/diktat-test-framework/pom.xml +++ b/diktat-test-framework/pom.xml @@ -57,45 +57,6 @@ org.jetbrains.kotlin kotlin-maven-plugin - - - compile - process-sources - - compile - - - - src/main/java - src/main/kotlin - src/main/resources - - - kotlinx-serialization - - - - - test-compile - process-test-sources - - test-compile - - - - src/test/java - src/test/kotlin - - - - - - - org.jetbrains.kotlin - kotlin-maven-serialization - ${kotlin.version} - - org.apache.maven.plugins diff --git a/pom.xml b/pom.xml index e3d68692e1..8ce992e1dd 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 1.2.5-SNAPSHOT pom - diktat + diktat-parent diKTat kotlin formatter and fixer https://www.cqfn.org/diKTat/ @@ -46,6 +46,7 @@ true official 1.4.1 + 0.3.5 0.47.1 5.9.1 1.9.1 @@ -82,6 +83,11 @@ + + org.cqfn.diktat + diktat + ${project.version} + com.squareup kotlinpoet @@ -132,6 +138,16 @@ kotlinx-serialization-json-jvm ${kotlinx.serialization.version} + + org.jetbrains.kotlinx + kotlinx-cli + ${kotlinx-cli.version} + + + org.jetbrains.kotlinx + kotlinx-cli-jvm + ${kotlinx-cli.version} + com.pinterest.ktlint ktlint-core @@ -148,6 +164,11 @@ ktlint-reporter-plain ${ktlint.version} + + com.pinterest.ktlint + ktlint-reporter-json + ${ktlint.version} + com.pinterest.ktlint ktlint-reporter-sarif @@ -155,7 +176,7 @@ com.pinterest.ktlint - ktlint-reporter-json + ktlint-reporter-checkstyle ${ktlint.version} @@ -203,6 +224,11 @@ log4j-slf4j-impl ${log4j.version} + + org.apache.logging.log4j + log4j-core + ${log4j.version} + io.github.petertrr kotlin-multiplatform-diff-jvm @@ -369,16 +395,51 @@ ${kotlin.version} 1.8 - - src/main/kotlin - src/main/resources - -Werror -opt-in=kotlin.RequiresOptIn true + + + compile + process-sources + + compile + + + + src/main/java + src/main/kotlin + src/main/resources + + + kotlinx-serialization + + + + + test-compile + process-test-sources + + test-compile + + + + src/test/java + src/test/kotlin + + + + + + + org.jetbrains.kotlin + kotlin-maven-serialization + ${kotlin.version} + + org.codehaus.mojo From 134a6e4e3c3bc77725ade1750dc23bd2e3f9b970 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Mon, 13 Mar 2023 19:30:35 +0300 Subject: [PATCH 05/33] Migration is in progress --- .../ruleset/rules/DiktatRuleSetFactory.kt | 6 +-- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 17 ++++--- .../cqfn/diktat/ruleset/utils/KtlintUtils.kt | 51 ++++++------------- .../ruleset/rules/OrderedRuleSetTest.kt | 14 ++--- .../org/cqfn/diktat/util/FixTestBase.kt | 4 +- .../org/cqfn/diktat/DiktatProcessCommand.kt | 38 +++++--------- .../diktat/ktlint/LintErrorCallbackWrapper.kt | 18 +++++-- pom.xml | 2 +- 8 files changed, 67 insertions(+), 83 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt index 061ed87598..f28c1c4c13 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt @@ -1,7 +1,7 @@ package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF -import com.pinterest.ktlint.core.KtLint +import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.Rule import kotlin.Function0 @@ -14,13 +14,13 @@ fun interface DiktatRuleSetFactory : Function0 { * of the rules have state or are not thread-safe - a new [DiktatRuleSet] must * be created). * - * For each invocation of [KtLint.lint] and [KtLint.format] the [DiktatRuleSet] + * For each invocation of [KtLintRuleEngine.lint] and [KtLintRuleEngine.format] the [DiktatRuleSet] * is retrieved. * This results in new instances of each [Rule] for each file being * processed. * As of that a [Rule] does not need to be thread-safe. * - * However, [KtLint.format] requires the [Rule] to be executed twice on a + * However, [KtLintRuleEngine.format] requires the [Rule] to be executed twice on a * file in case at least one violation has been autocorrected. * As the same [Rule] instance is reused for the second execution of the * [Rule], the state of the [Rule] is shared. diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 439f3efd40..de57704439 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -18,7 +18,6 @@ import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.isAnnotatedWithIgnoredAnnotation import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming -import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.ElementType.ANDAND import com.pinterest.ktlint.core.ast.ElementType.ANNOTATED_EXPRESSION @@ -66,6 +65,7 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtParameterList import org.jetbrains.kotlin.psi.psiUtil.children @@ -794,11 +794,16 @@ fun ASTNode.findAllNodesWithConditionOnLine( * * @return name of the file [this] node belongs to */ -fun ASTNode.getFilePath(): String = getRootNode().also { - require(it.elementType == FILE) { "Root node type is not FILE, but ${KtLint.FILE_PATH_USER_DATA_KEY} is present in user_data only in FILE nodes" } -}.getUserData(KtLint.FILE_PATH_USER_DATA_KEY).let { - requireNotNull(it) { "File path is not present in user data" } -} +fun ASTNode.getFilePath(): String = getRootNode() + .also { + require(it.elementType == FILE) { "Root node type is not FILE, but only ${KtFile::class} has file name" } + } + .psi + .let { it as? KtFile } + ?.name + .let { + requireNotNull(it) { "File path is not present in user data" } + } /** * checks that this one node is placed after the other node in code (by comparing lines of code where nodes start) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt index 9f74ede292..9389f3bdd6 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt @@ -5,8 +5,8 @@ package org.cqfn.diktat.ruleset.utils import org.cqfn.diktat.common.utils.loggerWithKtlintConfig -import com.pinterest.ktlint.core.KtLint -import com.pinterest.ktlint.core.KtLint.ExperimentalParams +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.RuleSetProviderV2 import mu.KotlinLogging @@ -20,29 +20,8 @@ val defaultCallback: (lintError: LintError, corrected: Boolean) -> Unit = { lint log.warn("Received linting error: $lintError") } -typealias LintErrorCallback = (LintError, Boolean) -> Unit - -/** - * Enables ignoring autocorrected errors when in "fix" mode (i.e. when - * [KtLint.format] is invoked). - * - * Before version 0.47, _Ktlint_ only reported non-corrected errors in "fix" - * mode. - * Now, this has changed. - * - * @receiver the instance of _Ktlint_ parameters. - * @return the instance with the [callback][ExperimentalParams.cb] modified in - * such a way that it ignores corrected errors. - * @see KtLint.format - * @see ExperimentalParams.cb - * @since 1.2.4 - */ -fun ExperimentalParams.ignoreCorrectedErrors(): ExperimentalParams = - copy(cb = { error: LintError, corrected: Boolean -> - if (!corrected) { - cb(error, false) - } - }) +typealias LintCallback = (LintError) -> Unit +typealias FormatCallback = (LintError, Boolean) -> Unit /** * @param ruleSetProviderRef @@ -56,17 +35,19 @@ fun format( ruleSetProviderRef: () -> RuleSetProviderV2, @Language("kotlin") text: String, fileName: String, - cb: LintErrorCallback = defaultCallback + cb: FormatCallback = defaultCallback ): String { val ruleProviders = ruleSetProviderRef().getRuleProviders() - return KtLint.format( - ExperimentalParams( - text = text, - ruleProviders = ruleProviders, - fileName = fileName.removeSuffix("_copy"), - script = fileName.removeSuffix("_copy").endsWith("kts"), - cb = cb, - debug = true, - ).ignoreCorrectedErrors() + val ktLintRuleEngine = KtLintRuleEngine( + ruleProviders = ruleProviders + ) + val code: Code = Code.CodeSnippet( + content = text, + script = fileName.removeSuffix("_copy").endsWith("kts") ) + return ktLintRuleEngine.format(code) { error: LintError, corrected: Boolean -> + if (!corrected) { + cb(error, false) + } + } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt index 6944a30242..ead440bc31 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt @@ -3,7 +3,8 @@ package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId import org.cqfn.diktat.ruleset.constants.EmitType import org.cqfn.diktat.util.TEST_FILE_NAME -import com.pinterest.ktlint.core.KtLint +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider import org.assertj.core.api.Assertions.assertThat @@ -92,12 +93,11 @@ class OrderedRuleSetTest { @Language("kotlin") val code = "fun foo() { }" - KtLint.lint( - KtLint.ExperimentalParams( - fileName = TEST_FILE_NAME, - text = code, - ruleProviders = ruleSet.ruleProviders, - cb = { _, _ -> }, + KtLintRuleEngine( + ruleProviders = ruleSet.ruleProviders + ).lint( + code = Code.CodeSnippet( + content = code ) ) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index 3f07ffa9b1..19ce921a9b 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -1,7 +1,7 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig -import org.cqfn.diktat.ruleset.utils.LintErrorCallback +import org.cqfn.diktat.ruleset.utils.FormatCallback import org.cqfn.diktat.ruleset.utils.defaultCallback import org.cqfn.diktat.ruleset.utils.format import org.cqfn.diktat.test.framework.processing.FileComparisonResult @@ -20,7 +20,7 @@ open class FixTestBase( resourceFilePath: String, ruleSupplier: (rulesConfigList: List) -> Rule, defaultRulesConfigList: List? = null, - cb: LintErrorCallback = defaultCallback, + cb: FormatCallback = defaultCallback, ) { /** * testComparatorUnit diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index 3998859905..b2b2e2448e 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -3,17 +3,14 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF import org.cqfn.diktat.ktlint.unwrap +import org.cqfn.diktat.ktlint.unwrapForLint import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactory import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 -import org.cqfn.diktat.ruleset.utils.isKotlinScript -import com.pinterest.ktlint.core.KtLint -import com.pinterest.ktlint.core.api.EditorConfigDefaults -import com.pinterest.ktlint.core.api.EditorConfigOverride +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import org.slf4j.Logger import org.slf4j.LoggerFactory import java.nio.file.Path -import kotlin.io.path.absolutePathString -import kotlin.io.path.readText /** * Command to run `diktat` @@ -22,42 +19,35 @@ import kotlin.io.path.readText */ class DiktatProcessCommand private constructor( val file: Path, - private val diktatRuleSetFactory: DiktatRuleSetFactory, + diktatRuleSetFactory: DiktatRuleSetFactory, private val callback: DiktatCallback, ) { private val isDebug: Boolean by lazy { LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).isDebugEnabled } + private val ktLintRuleEngine: KtLintRuleEngine = KtLintRuleEngine( + ruleProviders = DiktatRuleSetProviderV2(diktatRuleSetFactory).getRuleProviders(), + ) + + private val code: Code = Code.CodeFile( + file = file.toFile() + ) + /** * Run `diktat fix` using parameters from current command * * @return result of `diktat fix` */ - fun fix(): String = KtLint.format(ktLintParams()) + fun fix(): String = ktLintRuleEngine.format(code, callback.unwrap()) /** * Run `diktat check` using parameters from current command */ fun check() { - KtLint.lint(ktLintParams()) + ktLintRuleEngine.lint(code, callback.unwrapForLint()) } - private fun ktLintParams(): KtLint.ExperimentalParams = KtLint.ExperimentalParams( - fileName = file.absolutePathString(), - text = file.readText(Charsets.UTF_8), - ruleSets = emptySet(), - ruleProviders = DiktatRuleSetProviderV2(diktatRuleSetFactory).getRuleProviders(), - userData = emptyMap(), - cb = callback.unwrap(), - script = file.isKotlinScript(), - editorConfigPath = null, - debug = isDebug, - editorConfigDefaults = EditorConfigDefaults.emptyEditorConfigDefaults, - editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride, - isInvokedFromCli = false - ) - /** * Builder for [DiktatProcessCommand] * diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt index e768caebe2..7d784d6a76 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt @@ -5,18 +5,26 @@ package org.cqfn.diktat.ktlint import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.ruleset.utils.LintErrorCallback +import org.cqfn.diktat.ruleset.utils.FormatCallback +import org.cqfn.diktat.ruleset.utils.LintCallback /** - * @return [DiktatCallback] from KtLint [LintErrorCallback] + * @return [DiktatCallback] from KtLint [FormatCallback] */ -fun LintErrorCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> +fun FormatCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> this(error.unwrap(), isCorrected) } /** - * @return KtLint [LintErrorCallback] from [DiktatCallback] or exception + * @return KtLint [FormatCallback] from [DiktatCallback] or exception */ -fun DiktatCallback.unwrap(): LintErrorCallback = { error, isCorrected -> +fun DiktatCallback.unwrap(): FormatCallback = { error, isCorrected -> this.accept(error.wrap(), isCorrected) } + +/** + * @return KtLint [FormatCallback] from [DiktatCallback] or exception + */ +fun DiktatCallback.unwrapForLint(): LintCallback = { error -> + this.accept(error.wrap(), false) +} diff --git a/pom.xml b/pom.xml index 8ce992e1dd..f5dbfcf909 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ official 1.4.1 0.3.5 - 0.47.1 + 0.48.2 5.9.1 1.9.1 1.1.0 From 05977360db12646878496f6a14b41d117de6be49 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Mon, 13 Mar 2023 19:42:44 +0300 Subject: [PATCH 06/33] WIP --- .../diktat/ruleset/utils/AstNodeUtilsTest.kt | 17 +++++++++-------- .../org/cqfn/diktat/util/LintTestBase.kt | 18 +++++++++--------- .../kotlin/org/cqfn/diktat/util/TestUtils.kt | 5 ++++- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt index 3647aa16f3..9b3bae3708 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt @@ -8,8 +8,9 @@ package org.cqfn.diktat.ruleset.utils import org.cqfn.diktat.ruleset.constants.EmitType import org.cqfn.diktat.util.applyToCode +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine -import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider import com.pinterest.ktlint.core.ast.ElementType @@ -812,13 +813,13 @@ private class PrettyPrintingVisitor(private val elementType: IElementType, maxLevel: Int = -1, expected: String ) { - KtLint.lint( - KtLint.ExperimentalParams( - text = code, - ruleProviders = setOf(RuleProvider { - PrettyPrintingVisitor(elementType, level, maxLevel, expected) - }), - cb = { _, _ -> } + KtLintRuleEngine( + setOf(RuleProvider { + PrettyPrintingVisitor(elementType, level, maxLevel, expected) + }) + ).lint( + Code.CodeSnippet( + content = code ) ) } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt index d1473f4cee..33775147ec 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt @@ -1,7 +1,8 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig -import com.pinterest.ktlint.core.KtLint +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Rule import org.assertj.core.api.Assertions.assertThat @@ -74,16 +75,15 @@ open class LintTestBase(private val ruleSupplier: (rulesConfigList: List = mutableListOf() - KtLint.lint( - KtLint.ExperimentalParams( - fileName = actualFileName, + KtLintRuleEngine( + ruleProviders = DiktatRuleSetProvider4Test(ruleSupplier, + rulesConfigList ?: this.rulesConfigList).getRuleProviders(), + ).lint( + code = Code.CodeSnippet( + content = code, script = actualFileName.endsWith("kts"), - text = code, - ruleProviders = DiktatRuleSetProvider4Test(ruleSupplier, - rulesConfigList ?: this.rulesConfigList).getRuleProviders(), - cb = { lintError, _ -> lintErrors += lintError }, ) - ) + ) { lintError -> lintErrors += lintError } return lintErrors } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index d957b9544b..6a920b419c 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -5,8 +5,8 @@ package org.cqfn.diktat.util import org.cqfn.diktat.ruleset.constants.EmitType +import com.pinterest.ktlint.core.KtLintRuleEngine -import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider import org.assertj.core.api.Assertions.assertThat @@ -43,6 +43,9 @@ internal fun applyToCode(@Language("kotlin") code: String, applyToNode: (node: ASTNode, counter: AtomicInteger) -> Unit ) { val counter = AtomicInteger(0) + KtLintRuleEngine( + + ) KtLint.lint( KtLint.ExperimentalParams( text = code, From 76ee541d6ec320d7fdd6a2796c0cea1e5b341634 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 14 Mar 2023 11:27:10 +0300 Subject: [PATCH 07/33] WIP #2 --- .../diktat/plugin/maven/DiktatBaseMojo.kt | 17 ++++------- .../kotlin/org/cqfn/diktat/util/TestUtils.kt | 29 +++++++++---------- pom.xml | 2 +- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index f0ae9ccee1..aafb407dae 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -1,7 +1,3 @@ -@file:Suppress( - "Deprecation" -) - package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand @@ -10,9 +6,10 @@ import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Reporter -import com.pinterest.ktlint.core.RuleExecutionException import com.pinterest.ktlint.core.api.Baseline import com.pinterest.ktlint.core.api.Baseline.Status.VALID +import com.pinterest.ktlint.core.api.KtLintParseException +import com.pinterest.ktlint.core.api.KtLintRuleException import com.pinterest.ktlint.core.api.containsLintError import com.pinterest.ktlint.core.api.loadBaseline import com.pinterest.ktlint.reporter.baseline.BaselineReporter @@ -215,14 +212,12 @@ abstract class DiktatBaseMojo : AbstractMojo() { ) ) reporterImpl.after(file.absolutePath) - } catch (e: RuleExecutionException) { - /* - * https://github.com/pinterest/ktlint/issues/1710: - * no alternative in KtLint 0.47; - * may get changed to `KtLintRuleExecutionException` in KtLint 0.48. - */ + } catch (e: KtLintRuleException) { log.error("Unhandled exception during rule execution: ", e) throw MojoExecutionException("Unhandled exception during rule execution", e) + } catch (e: KtLintParseException) { + log.error("Unhandled exception parsing a file: ", e) + throw MojoExecutionException("Unhandled exception parsing a file", e) } } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index 6a920b419c..e2e3c89dcb 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -6,7 +6,7 @@ package org.cqfn.diktat.util import org.cqfn.diktat.ruleset.constants.EmitType import com.pinterest.ktlint.core.KtLintRuleEngine - +import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider import org.assertj.core.api.Assertions.assertThat @@ -44,22 +44,19 @@ internal fun applyToCode(@Language("kotlin") code: String, ) { val counter = AtomicInteger(0) KtLintRuleEngine( - - ) - KtLint.lint( - KtLint.ExperimentalParams( - text = code, - ruleProviders = setOf(RuleProvider { - object : Rule("test:astnode-utils-test") { - override fun beforeVisitChildNodes(node: ASTNode, - autoCorrect: Boolean, - emit: EmitType - ) { - applyToNode(node, counter) - } + ruleProviders = setOf(RuleProvider { + object : Rule("test:astnode-utils-test") { + override fun beforeVisitChildNodes(node: ASTNode, + autoCorrect: Boolean, + emit: EmitType + ) { + applyToNode(node, counter) } - }), - cb = { _, _ -> } + } + }), + ).lint( + code = Code.CodeSnippet( + content = code ) ) assertThat(counter.get()) diff --git a/pom.xml b/pom.xml index f5dbfcf909..1f98588fd3 100644 --- a/pom.xml +++ b/pom.xml @@ -396,7 +396,7 @@ 1.8 - -Werror + -opt-in=kotlin.RequiresOptIn From 78adb2aba422b92553fa507cce7064fd36264379 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 14 Mar 2023 13:05:30 +0300 Subject: [PATCH 08/33] WIP --- diktat-rules/pom.xml | 4 ++-- .../org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 10 +++++----- pom.xml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml index 37e3b944d2..d2b62b441f 100644 --- a/diktat-rules/pom.xml +++ b/diktat-rules/pom.xml @@ -92,7 +92,7 @@ - ${project.basedir}/src/main/kotlin/generated + ${project.basedir}/src/generated/kotlin @@ -114,7 +114,7 @@ org.cqfn.diktat.ruleset.generation.GenerationKt - ${project.build.sourceDirectory} + ${project.basedir}/src/generated/kotlin ${project.basedir}/src/test/resources diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index de57704439..22ef3fcb5d 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -795,14 +795,14 @@ fun ASTNode.findAllNodesWithConditionOnLine( * @return name of the file [this] node belongs to */ fun ASTNode.getFilePath(): String = getRootNode() - .also { - require(it.elementType == FILE) { "Root node type is not FILE, but only ${KtFile::class} has file name" } + .takeIf { + it.elementType == FILE } - .psi - .let { it as? KtFile } + ?.psi + ?.let { it as? KtFile } ?.name .let { - requireNotNull(it) { "File path is not present in user data" } + requireNotNull(it) { "Root node type is not FILE, but only ${KtFile::class} has file name" } } /** diff --git a/pom.xml b/pom.xml index 1f98588fd3..f5dbfcf909 100644 --- a/pom.xml +++ b/pom.xml @@ -396,7 +396,7 @@ 1.8 - + -Werror -opt-in=kotlin.RequiresOptIn From 2f635b8facbcd2ae3213c363d94439f3a1d3c8cf Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 15 Mar 2023 20:09:32 +0300 Subject: [PATCH 09/33] reverted changes related to codegen --- diktat-rules/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml index d2b62b441f..37e3b944d2 100644 --- a/diktat-rules/pom.xml +++ b/diktat-rules/pom.xml @@ -92,7 +92,7 @@ - ${project.basedir}/src/generated/kotlin + ${project.basedir}/src/main/kotlin/generated @@ -114,7 +114,7 @@ org.cqfn.diktat.ruleset.generation.GenerationKt - ${project.basedir}/src/generated/kotlin + ${project.build.sourceDirectory} ${project.basedir}/src/test/resources From ea785760c8fdc541091c6b793d6854fcead9095e Mon Sep 17 00:00:00 2001 From: Andrey Shcheglov Date: Thu, 16 Mar 2023 17:18:37 +0300 Subject: [PATCH 10/33] Extract common integration test code (#1629) ### What's done: * Common code extracted in preparation to add more integration tests. --- .github/workflows/build_and_test.yml | 2 +- .../cqfn/diktat/common/ktlint/KtLintUtils.kt | 22 +++ .../ruleset/chapter2/HeaderCommentRuleTest.kt | 8 +- .../kotlin/org/cqfn/diktat/util/TestUtils.kt | 72 +++++++- diktat-ruleset/pom.xml | 10 +- .../ruleset/smoke/DiktatSaveSmokeTest.kt | 160 +++++++----------- .../ruleset/smoke/DiktatSmokeTestUtils.kt | 62 +++++++ .../smoke/src/main/kotlin/Example5Expected.kt | 2 +- .../diktat/test/framework/util/TestUtils.kt | 81 ++++++++- 9 files changed, 309 insertions(+), 110 deletions(-) create mode 100644 diktat-common/src/main/kotlin/org/cqfn/diktat/common/ktlint/KtLintUtils.kt create mode 100644 diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestUtils.kt diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 404a8c3eaa..cfb91090c3 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -30,7 +30,7 @@ jobs: with: files: '**/target/site/jacoco*/jacoco.xml,**/reports/jacoco/**/*.xml' flags: unittests - fail_ci_if_error: true # optional (default = false) + fail_ci_if_error: false # optional (default = false) - name: Upload diktat jar uses: actions/upload-artifact@v3 with: diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/ktlint/KtLintUtils.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/ktlint/KtLintUtils.kt new file mode 100644 index 0000000000..4e45c72d02 --- /dev/null +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/ktlint/KtLintUtils.kt @@ -0,0 +1,22 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + +package org.cqfn.diktat.common.ktlint + +private val ktlintRuleSetIds: List by lazy { + listOf( + "standard", + "experimental", + "test", + "custom" + ) +} + +/** + * Contains the necessary value of the `--disabled_rules` _KtLint_ argument. + */ +val ktlintDisabledRulesArgument: String by lazy { + ktlintRuleSetIds.joinToString( + prefix = "--disabled_rules=", + separator = "," + ) +} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt index 1b030758ea..4c57aa6cee 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt @@ -221,7 +221,7 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) { lintMethod( """ /* - * Copyright (c) 2022 My Company, Ltd. All rights reserved. + * Copyright (c) $curYear My Company, Ltd. All rights reserved. */ /** * Very useful description, why this file has two classes @@ -244,7 +244,7 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) { lintMethod( """ /* - * Copyright (c) My Company, Ltd. 2012-2022. All rights reserved. + * Copyright (c) My Company, Ltd. 2012-$curYear. All rights reserved. */ /** * Very useful description, why this file has two classes @@ -267,7 +267,7 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) { lintMethod( """ /* - Copyright (c) My Company, Ltd. 2021-2022. All rights reserved. + Copyright (c) My Company, Ltd. 2021-$curYear. All rights reserved. */ /** * Very useful description, why this file has two classes @@ -290,7 +290,7 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) { lintMethod( """ /* - * Copyright (c) My Company, Ltd. 2002-2022. All rights reserved. + * Copyright (c) My Company, Ltd. 2002-$curYear. All rights reserved. */ /** * Very useful description, why this file has two classes diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index d957b9544b..25c336b026 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -14,10 +14,19 @@ import org.assertj.core.api.Assertions.fail import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import java.io.Reader import java.util.concurrent.atomic.AtomicInteger +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind.EXACTLY_ONCE +import kotlin.contracts.contract internal const val TEST_FILE_NAME = "TestFileName.kt" +private val debuggerPromptPrefixes: Array = arrayOf( + "Listening for transport dt_socket at address: ", + "Listening for transport dt_shmem at address: ", +) + /** * Casts a nullable value to a non-`null` one, similarly to the `!!` * operator. @@ -25,8 +34,67 @@ internal const val TEST_FILE_NAME = "TestFileName.kt" * @param lazyFailureMessage the message to evaluate in case of a failure. * @return a non-`null` value. */ -internal fun T?.assertNotNull(lazyFailureMessage: () -> String = { "Expecting actual not to be null" }): T = - this ?: fail(lazyFailureMessage()) +@OptIn(ExperimentalContracts::class) +internal fun T?.assertNotNull(lazyFailureMessage: () -> String = { "Expecting actual not to be null" }): T { + contract { + returns() implies (this@assertNotNull != null) + } + + return this ?: fail(lazyFailureMessage()) +} + +/** + * Calls the [block] callback giving it a sequence of all the lines in this file + * and closes the reader once the processing is complete. + * + * If [filterDebuggerPrompt] is `true`, the JVM debugger prompt is filtered out + * from the sequence of lines before it is consumed by [block]. + * + * If [filterDebuggerPrompt] is `false`, this function behaves exactly as the + * overloaded function from the standard library. + * + * @param filterDebuggerPrompt whether the JVM debugger prompt should be + * filtered out. + * @param block the callback which consumes the lines produced by this [Reader]. + * @return the value returned by [block]. + */ +@OptIn(ExperimentalContracts::class) +internal fun Reader.useLines( + filterDebuggerPrompt: Boolean, + block: (Sequence) -> T, +): T { + contract { + callsInPlace(block, EXACTLY_ONCE) + } + + return when { + filterDebuggerPrompt -> { + /* + * Transform the line consumer. + */ + { lines -> + lines.filterNot(String::isDebuggerPrompt).let(block) + } + } + + else -> block + }.let(this::useLines) +} + +private fun String.isDebuggerPrompt(printIfTrue: Boolean = true): Boolean { + val isDebuggerPrompt = debuggerPromptPrefixes.any { prefix -> + this.startsWith(prefix) + } + if (isDebuggerPrompt && printIfTrue) { + /* + * Print the prompt to the standard out, + * so that the IDE can attach to the debugger. + */ + @Suppress("DEBUG_PRINT") + println(this) + } + return isDebuggerPrompt +} /** * This utility function lets you run arbitrary code on every node of given [code]. diff --git a/diktat-ruleset/pom.xml b/diktat-ruleset/pom.xml index 6b169771fb..1b88b88e35 100644 --- a/diktat-ruleset/pom.xml +++ b/diktat-ruleset/pom.xml @@ -79,6 +79,12 @@ mockito-all test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + @@ -142,7 +148,7 @@ maven-surefire-plugin - **/DiktatSaveSmokeTest.* + **/Diktat?*SmokeTest.* @@ -151,7 +157,7 @@ maven-failsafe-plugin - **/DiktatSaveSmokeTest.* + **/Diktat?*SmokeTest.* diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt index d85eb1dbfe..d7831e951b 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt @@ -1,15 +1,16 @@ package org.cqfn.diktat.ruleset.smoke import org.cqfn.diktat.common.utils.loggerWithKtlintConfig +import org.cqfn.diktat.test.framework.processing.TestComparatorUnit +import org.cqfn.diktat.test.framework.util.checkForkedJavaHome import org.cqfn.diktat.test.framework.util.deleteIfExistsRecursively import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently -import org.cqfn.diktat.test.framework.util.isSameJavaHomeAs -import org.cqfn.diktat.test.framework.util.prependPath -import org.cqfn.diktat.test.framework.util.retry +import org.cqfn.diktat.test.framework.util.inheritJavaHome +import org.cqfn.diktat.test.framework.util.isWindows +import org.cqfn.diktat.test.framework.util.temporaryDirectory import com.pinterest.ktlint.core.LintError import mu.KotlinLogging -import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.assertj.core.api.SoftAssertions.assertSoftly import org.junit.jupiter.api.AfterAll @@ -21,16 +22,12 @@ import java.net.URL import java.nio.file.Path import kotlin.io.path.Path import kotlin.io.path.absolute -import kotlin.io.path.absolutePathString import kotlin.io.path.copyTo import kotlin.io.path.createDirectories import kotlin.io.path.div import kotlin.io.path.exists import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.outputStream import kotlin.io.path.readText -import kotlin.io.path.relativeTo -import kotlin.system.measureNanoTime @DisabledOnOs(OS.MAC) class DiktatSaveSmokeTest : DiktatSmokeTestBase() { @@ -47,7 +44,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { override fun assertUnfixedLintErrors(lintErrorsConsumer: (List) -> Unit) = Unit /** - * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] + * @param testPath path to file with code that will be transformed by formatter, relative to [TestComparatorUnit.resourceFilePath] * @param configFilePath path of diktat-analysis file */ @Suppress("TOO_LONG_FUNCTION") @@ -76,9 +73,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { /* * Inherit JAVA_HOME for the child process. */ - val javaHome = System.getProperty("java.home") - environment()["JAVA_HOME"] = javaHome - prependPath(Path(javaHome) / "bin") + inheritJavaHome() /* * On Windows, ktlint is often unable to relativize paths @@ -87,12 +82,8 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { * So let's force the temporary directory to be the * sub-directory of the project root. */ - if (System.getProperty("os.name").startsWith("Windows")) { - val tempDirectory = baseDirectoryPath / ".save-cli" - tempDirectory.createDirectories() - val tempDirectoryPath = tempDirectory.absolutePathString() - environment()["TMP"] = tempDirectoryPath - environment()["TEMP"] = tempDirectoryPath + if (System.getProperty("os.name").isWindows()) { + temporaryDirectory(baseDirectoryPath / WINDOWS_TEMP_DIRECTORY) } } @@ -117,29 +108,32 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { } /** - * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] + * @param testPath path to file with code that will be transformed by formatter, relative to [TestComparatorUnit.resourceFilePath] * @return ProcessBuilder */ private fun createProcessBuilderWithCmd(testPath: String): ProcessBuilder { val savePath = "$BASE_DIRECTORY/${getSaveForCurrentOs()}" - - val systemName = System.getProperty("os.name") - val result = when { - systemName.startsWith("Linux", ignoreCase = true) || systemName.startsWith("Mac", ignoreCase = true) -> - ProcessBuilder("sh", "-c", "chmod 777 $savePath ; ./$savePath $BASE_DIRECTORY/src/main/kotlin $testPath --log all") - else -> ProcessBuilder(savePath, "$BASE_DIRECTORY/src/main/kotlin", testPath, "--log", "all") + val saveArgs = arrayOf( + "$BASE_DIRECTORY/src/main/kotlin", + testPath, + "--log", + "all" + ) + + return when { + System.getProperty("os.name").isWindows() -> arrayOf(savePath, *saveArgs) + else -> arrayOf("sh", "-c", "chmod 777 $savePath ; ./$savePath ${saveArgs.joinToString(" ")}") + }.let { args -> + ProcessBuilder(*args) } - return result } companion object { @Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") private val logger = KotlinLogging.loggerWithKtlintConfig { } private const val BASE_DIRECTORY = "src/test/resources/test/smoke" - private const val BUILD_DIRECTORY = "target" - private const val FAT_JAR_GLOB = "diktat-*.jar" - private const val KTLINT_VERSION = "0.47.1" private const val SAVE_VERSION: String = "0.3.4" + private const val WINDOWS_TEMP_DIRECTORY = ".save-cli" private val baseDirectoryPath = Path(BASE_DIRECTORY).absolute() private fun getSaveForCurrentOs(): String { @@ -148,95 +142,67 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { return when { osName.startsWith("Linux", ignoreCase = true) -> "save-$SAVE_VERSION-linuxX64.kexe" osName.startsWith("Mac", ignoreCase = true) -> "save-$SAVE_VERSION-macosX64.kexe" - osName.startsWith("Windows", ignoreCase = true) -> "save-$SAVE_VERSION-mingwX64.exe" + osName.isWindows() -> "save-$SAVE_VERSION-mingwX64.exe" else -> fail("SAVE doesn't support $osName (version ${System.getProperty("os.version")})") } } - @Suppress("FLOAT_IN_ACCURATE_CALCULATIONS") - private fun downloadFile(from: URL, to: Path) { - logger.info { - "Downloading $from to ${to.relativeTo(baseDirectoryPath)}..." - } - - val attempts = 5 - - val lazyDefault: (Throwable) -> Unit = { error -> - fail("Failure downloading $from after $attempts attempt(s)", error) - } - - retry(attempts, lazyDefault = lazyDefault) { - from.openStream().use { source -> - to.outputStream().use { target -> - val bytesCopied: Long - val timeNanos = measureNanoTime { - bytesCopied = source.copyTo(target) - } - logger.info { - "$bytesCopied byte(s) copied in ${timeNanos / 1000 / 1e3} ms." - } - } - } - } - } + private fun downloadFile(from: URL, to: Path) = + downloadFile(from, to, baseDirectoryPath) @BeforeAll @JvmStatic - @Suppress("AVOID_NULL_CHECKS") internal fun beforeAll() { - val forkedJavaHome = System.getenv("JAVA_HOME") - if (forkedJavaHome != null) { - val javaHome = System.getProperty("java.home") - if (javaHome != null && !Path(javaHome).isSameJavaHomeAs(Path(forkedJavaHome))) { - logger.warn { - "Current JDK home is $javaHome. Forked tests may use a different JDK at $forkedJavaHome." - } - } - logger.warn { - "Make sure JAVA_HOME ($forkedJavaHome) points to a Java 8 or Java 11 home. Java 17 is not yet supported." + assertSoftly { softly -> + checkForkedJavaHome() + + logger.info { + "The base directory for the smoke test is $baseDirectoryPath." } - } - logger.info { - "The base directory for the smoke test is $baseDirectoryPath." + /* + * The fat JAR should reside in the same directory as `ktlint` and + * `save*` and be named `diktat.jar` + * (see `diktat-rules/src/test/resources/test/smoke/save.toml`). + */ + val buildDirectory = Path(BUILD_DIRECTORY) + softly.assertThat(buildDirectory) + .isDirectory + val diktatFrom = buildDirectory + .takeIf(Path::exists) + ?.listDirectoryEntries(DIKTAT_FAT_JAR_GLOB) + ?.singleOrNull() + softly.assertThat(diktatFrom) + .describedAs(diktatFrom?.toString() ?: "$BUILD_DIRECTORY/$DIKTAT_FAT_JAR_GLOB") + .isNotNull + .isRegularFile + + val diktat = baseDirectoryPath / DIKTAT_FAT_JAR + val save = baseDirectoryPath / getSaveForCurrentOs() + val ktlint = baseDirectoryPath / KTLINT_FAT_JAR + + downloadFile(URL("https://github.com/saveourtool/save-cli/releases/download/v$SAVE_VERSION/${getSaveForCurrentOs()}"), save) + downloadFile(URL("https://github.com/pinterest/ktlint/releases/download/$KTLINT_VERSION/ktlint"), ktlint) + + diktatFrom?.copyTo(diktat, overwrite = true) } - - /* - * The fat JAR should reside in the same directory as `ktlint` and - * `save*` and be named `diktat.jar` - * (see `diktat-rules/src/test/resources/test/smoke/save.toml`). - */ - val diktatFrom = Path(BUILD_DIRECTORY) - .takeIf(Path::exists) - ?.listDirectoryEntries(FAT_JAR_GLOB) - ?.singleOrNull() - assertThat(diktatFrom) - .describedAs(diktatFrom?.toString() ?: "$BUILD_DIRECTORY/$FAT_JAR_GLOB") - .isNotNull - .isRegularFile - - val diktat = baseDirectoryPath / "diktat.jar" - val save = baseDirectoryPath / getSaveForCurrentOs() - val ktlint = baseDirectoryPath / "ktlint" - - downloadFile(URL("https://github.com/saveourtool/save-cli/releases/download/v$SAVE_VERSION/${getSaveForCurrentOs()}"), save) - downloadFile(URL("https://github.com/pinterest/ktlint/releases/download/$KTLINT_VERSION/ktlint"), ktlint) - - diktatFrom?.copyTo(diktat, overwrite = true) } @AfterAll @JvmStatic internal fun afterAll() { - val diktat = baseDirectoryPath / "diktat.jar" + val diktat = baseDirectoryPath / DIKTAT_FAT_JAR val save = baseDirectoryPath / getSaveForCurrentOs() - val ktlint = baseDirectoryPath / "ktlint" - val tempDirectory = baseDirectoryPath / ".save-cli" + val ktlint = baseDirectoryPath / KTLINT_FAT_JAR diktat.deleteIfExistsSilently() ktlint.deleteIfExistsSilently() save.deleteIfExistsSilently() - tempDirectory.deleteIfExistsRecursively() + + if (System.getProperty("os.name").isWindows()) { + val tempDirectory = baseDirectoryPath / WINDOWS_TEMP_DIRECTORY + tempDirectory.deleteIfExistsRecursively() + } } } } diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestUtils.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestUtils.kt new file mode 100644 index 0000000000..189535b646 --- /dev/null +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestUtils.kt @@ -0,0 +1,62 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + +package org.cqfn.diktat.ruleset.smoke + +import org.cqfn.diktat.common.utils.loggerWithKtlintConfig +import org.cqfn.diktat.test.framework.util.retry +import mu.KotlinLogging +import org.assertj.core.api.Assertions.fail +import java.net.URL +import java.nio.file.Path +import kotlin.io.path.outputStream +import kotlin.io.path.relativeToOrSelf +import kotlin.system.measureNanoTime + +internal const val BUILD_DIRECTORY = "target" +internal const val DIKTAT_FAT_JAR = "diktat.jar" +internal const val DIKTAT_FAT_JAR_GLOB = "diktat-*.jar" +internal const val KTLINT_FAT_JAR = "ktlint" +internal const val KTLINT_VERSION = "0.47.1" + +@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") +private val logger = KotlinLogging.loggerWithKtlintConfig { } + +/** + * Downloads the file from a remote URL, retrying if necessary. + * + * @param from the remote URL to download from. + * @param to the target path. + * @param baseDirectory the directory against which [to] should be relativized + * if it's absolute. + */ +@Suppress("FLOAT_IN_ACCURATE_CALCULATIONS") +internal fun downloadFile( + from: URL, + to: Path, + baseDirectory: Path, +) { + logger.info { + "Downloading $from to ${to.relativeToOrSelf(baseDirectory)}..." + } + + @Suppress("MAGIC_NUMBER") + val attempts = 5 + + val lazyDefault: (Throwable) -> Unit = { error -> + fail("Failure downloading $from after $attempts attempt(s)", error) + } + + retry(attempts, lazyDefault = lazyDefault) { + from.openStream().use { source -> + to.outputStream().use { target -> + val bytesCopied: Long + val timeNanos = measureNanoTime { + bytesCopied = source.copyTo(target) + } + logger.info { + "$bytesCopied byte(s) copied in ${timeNanos / 1000 / 1e3} ms." + } + } + } + } +} diff --git a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt index baa4d420ed..9a5929c2ca 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt +++ b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt @@ -1,6 +1,6 @@ // ;warn:$line:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: Example5Expected.kt vs Some{{.*}} /* - Copyright 2018-2022 John Doe. + Copyright 2018-2023 John Doe. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt index 8d65e2a70a..bd0f40ad2e 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt @@ -18,16 +18,22 @@ import java.nio.file.NoSuchFileException import java.nio.file.Path import java.nio.file.SimpleFileVisitor import java.nio.file.attribute.BasicFileAttributes +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract +import kotlin.io.path.Path import kotlin.io.path.absolute +import kotlin.io.path.absolutePathString import kotlin.io.path.bufferedReader +import kotlin.io.path.createDirectories import kotlin.io.path.deleteExisting import kotlin.io.path.deleteIfExists +import kotlin.io.path.div import kotlin.io.path.isDirectory import kotlin.io.path.isSameFileAs @Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") -private val log = KotlinLogging.loggerWithKtlintConfig {} +private val logger = KotlinLogging.loggerWithKtlintConfig {} /** * Deletes the file if it exists, retrying as necessary if the file is @@ -54,7 +60,7 @@ fun Path.deleteIfExistsSilently() { } if (!deleted) { - log.warn { + logger.warn { "File \"${absolute()}\" not deleted after $attempts attempt(s)." } } @@ -202,7 +208,7 @@ fun ProcessBuilder.prependPath(pathEntry: Path) { * Keys of the Java interface to the environment are not ("PATH" != "Path"). * This is an attempt to work around the inconsistency. */ - System.getProperty("os.name").startsWith("Windows") -> environment.keys.firstOrNull { key -> + System.getProperty("os.name").isWindows() -> environment.keys.firstOrNull { key -> key.equals(defaultPathKey, ignoreCase = true) } ?: defaultWindowsPathKey @@ -220,6 +226,55 @@ fun ProcessBuilder.prependPath(pathEntry: Path) { environment[pathKey] = newPath } +/** + * Inherits the home of the current JVM (by setting `JAVA_HOME` and adding it to + * the `PATH`) for the children of this process builder. + */ +fun ProcessBuilder.inheritJavaHome() { + val javaHome = System.getProperty("java.home") + environment()["JAVA_HOME"] = javaHome + prependPath(Path(javaHome) / "bin") +} + +/** + * Changes the temporary directory for the children of this process builder. + * + * @param temporaryDirectory the new temporary directory (created automatically, + * scheduled for removal at JVM exit). + */ +fun ProcessBuilder.temporaryDirectory(temporaryDirectory: Path) { + temporaryDirectory.createDirectories().tryToDeleteOnExit() + + /* + * On UNIX, TMPDIR is the canonical name + */ + val environmentVariables: Sequence = when { + System.getProperty("os.name").isWindows() -> sequenceOf("TMP", "TEMP") + else -> sequenceOf("TMPDIR") + } + + val environment = environment() + + val value = temporaryDirectory.absolutePathString() + environmentVariables.forEach { name -> + environment[name] = value + } +} + +/** + * @receiver the value of `os.name` system property. + * @return `true` if the value of `os.name` system property starts with + * "Windows", `false` otherwise. + */ +@OptIn(ExperimentalContracts::class) +fun String?.isWindows(): Boolean { + contract { + returns(true) implies (this@isWindows != null) + } + + return this != null && startsWith("Windows") +} + /** * Retries the execution of the [block]. * @@ -258,3 +313,23 @@ fun retry( return lazyDefault(lastError ?: Exception("The block was never executed")) } + +/** + * Checks whether the current JVM's home matches the `JAVA_HOME` environment + * variable. + */ +@Suppress("AVOID_NULL_CHECKS") +fun checkForkedJavaHome() { + val forkedJavaHome = System.getenv("JAVA_HOME") + if (forkedJavaHome != null) { + val javaHome = System.getProperty("java.home") + if (javaHome != null && !Path(javaHome).isSameJavaHomeAs(Path(forkedJavaHome))) { + logger.warn { + "Current JDK home is $javaHome. Forked tests may use a different JDK at $forkedJavaHome." + } + } + logger.warn { + "Make sure JAVA_HOME ($forkedJavaHome) points to a Java 8 or Java 11 home. Java 17 is not yet supported." + } + } +} From 2ff1a1f40766d67d79f3eb2a8a42c452326fd8e0 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Mon, 27 Mar 2023 11:56:04 +0300 Subject: [PATCH 11/33] diktatFix --- .../main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt | 2 -- .../src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt | 2 -- gradle/libs.versions.toml | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 4227b4163b..099f476b9b 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -6,8 +6,6 @@ import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Reporter -import com.pinterest.ktlint.core.api.Baseline -import com.pinterest.ktlint.core.api.Baseline.Status.VALID import com.pinterest.ktlint.core.api.KtLintParseException import com.pinterest.ktlint.core.api.KtLintRuleException import com.pinterest.ktlint.core.api.containsLintError diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index b2b2e2448e..8451ee4ff0 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -25,11 +25,9 @@ class DiktatProcessCommand private constructor( private val isDebug: Boolean by lazy { LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).isDebugEnabled } - private val ktLintRuleEngine: KtLintRuleEngine = KtLintRuleEngine( ruleProviders = DiktatRuleSetProviderV2(diktatRuleSetFactory).getRuleProviders(), ) - private val code: Code = Code.CodeFile( file = file.toFile() ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f98d588b81..4bebb42daf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.10" serialization = "1.5.0" -ktlint = "0.46.1" +ktlint = "0.48.2" junit = "5.9.1" junit-platfrom = "1.9.1" guava = "31.1-jre" From 4696d736f9f027b5141536cb4f7100496318da40 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Mon, 27 Mar 2023 13:30:40 +0300 Subject: [PATCH 12/33] fix merge issues --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/diktat.yml | 2 +- .github/workflows/diktat_snapshot.yml | 2 +- .../plugin/gradle/DiktatJavaExecTaskTest.kt | 1 - diktat-maven-plugin/build.gradle.kts | 1 + .../cqfn/diktat/plugin/maven/DiktatBaseMojo.kt | 8 ++++---- .../org/cqfn/diktat/ruleset/rules/DiktatRule.kt | 2 +- .../diktat/ruleset/rules/chapter6/RunInScript.kt | 2 +- .../diktat/ruleset/rules/OrderedRuleSetTest.kt | 2 +- .../diktat/ruleset/utils/AstNodeUtilsTest.kt | 7 ++++--- .../kotlin/org/cqfn/diktat/util/TestUtils.kt | 2 +- diktat-ruleset/build.gradle.kts | 16 +++++++++------- gradle/libs.versions.toml | 12 +++++++++--- 13 files changed, 35 insertions(+), 26 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index feeb74fbe0..0ca1abf585 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -7,10 +7,10 @@ name: "CodeQL" on: push: - branches: [ master, feature/ktlint-wrapper ] + branches: [master] pull_request: # The branches below must be a subset of the branches above - branches: [ master, feature/ktlint-wrapper ] + branches: [master] schedule: - cron: '0 20 * * 5' diff --git a/.github/workflows/diktat.yml b/.github/workflows/diktat.yml index a74a5e28d8..5ac6eeab5d 100644 --- a/.github/workflows/diktat.yml +++ b/.github/workflows/diktat.yml @@ -4,7 +4,7 @@ on: push: branches: [ master ] pull_request: - branches: [ master, feature/ktlint-wrapper ] + branches: [ master ] jobs: diktat_check: diff --git a/.github/workflows/diktat_snapshot.yml b/.github/workflows/diktat_snapshot.yml index cb4b494af8..6ab4b7e62f 100644 --- a/.github/workflows/diktat_snapshot.yml +++ b/.github/workflows/diktat_snapshot.yml @@ -4,7 +4,7 @@ on: push: branches: [ master ] pull_request: - branches: [ master, feature/ktlint-wrapper ] + branches: [ master ] jobs: diktat_snapshot_check: diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt index 283dd51e50..00a4f08bbf 100644 --- a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt @@ -269,6 +269,5 @@ class DiktatJavaExecTaskTest { companion object { private const val DIKTAT_CHECK_TASK = "diktatCheck" - private const val DISABLED_RULES = "--disabled_rules=standard,experimental,test,custom" } } diff --git a/diktat-maven-plugin/build.gradle.kts b/diktat-maven-plugin/build.gradle.kts index ba822027cb..42b5f15eee 100644 --- a/diktat-maven-plugin/build.gradle.kts +++ b/diktat-maven-plugin/build.gradle.kts @@ -11,6 +11,7 @@ plugins { dependencies { implementation(libs.maven.plugin.api) + implementation(projects.diktatRuleset) compileOnly(libs.maven.plugin.annotations) compileOnly(libs.maven.core) diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 43a0ae85fa..455fc6e31c 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -2,11 +2,11 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Reporter +import com.pinterest.ktlint.core.api.Baseline import com.pinterest.ktlint.core.api.KtLintParseException import com.pinterest.ktlint.core.api.KtLintRuleException import com.pinterest.ktlint.core.api.containsLintError @@ -108,7 +108,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { ) val baselineResults = baseline?.let { loadBaseline(it.absolutePath) } - ?: CurrentBaseline(emptyMap(), false) + ?: Baseline(emptyMap(), Baseline.Status.NOT_FOUND) reporterImpl = resolveReporter(baselineResults) reporterImpl.beforeAll() val lintErrors: MutableList = mutableListOf() @@ -125,7 +125,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { } } - private fun resolveReporter(baselineResults: CurrentBaseline): Reporter { + private fun resolveReporter(baselineResults: Baseline): Reporter { val output = if (this.output.isBlank()) { if (this.githubActions) { // need to set user.home specially for ktlint, so it will be able to put a relative path URI in SARIF @@ -153,7 +153,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { } } - return if (baselineResults.baselineGenerationNeeded) { + return if (baselineResults.status == Baseline.Status.NOT_FOUND) { val baselineReporter = BaselineReporter(PrintStream(FileOutputStream(baseline, true))) return Reporter.from(actualReporter, baselineReporter) } else { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt index d620ec8ffa..0924b2b8be 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt @@ -48,7 +48,7 @@ abstract class DiktatRule( lateinit var emitWarn: EmitType @Suppress("TooGenericExceptionThrown") - final override fun visit( + final override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: EmitType diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt index c00cfc01eb..bd4259cecf 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt @@ -27,7 +27,7 @@ class RunInScript(private val configRules: List) : Rule(NAME_ID.qua private var isFixMode: Boolean = false private lateinit var emitWarn: EmitType - override fun visit( + override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: EmitType diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt index a82456443c..5f27170706 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/rules/OrderedRuleSetTest.kt @@ -160,7 +160,7 @@ class OrderedRuleSetTest { visitorModifiers: Set = emptySet(), onVisit: (Rule) -> Unit = { } ): Rule = object : Rule(id.qualifiedWithRuleSetId(), visitorModifiers) { - override fun visit( + override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: EmitType diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt index 8f0a1cb455..9989e06fe4 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtilsTest.kt @@ -793,9 +793,10 @@ private class PrettyPrintingVisitor(private val elementType: IElementType, private val maxLevel: Int, private val expected: String ) : Rule("test:print-ast") { - override fun visit(node: ASTNode, - autoCorrect: Boolean, - emit: EmitType + override fun beforeVisitChildNodes( + node: ASTNode, + autoCorrect: Boolean, + emit: EmitType ) { if (node.elementType == elementType) { Assertions.assertEquals( diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index bce3e53cb6..56c03eea00 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -114,7 +114,7 @@ internal fun applyToCode(@Language("kotlin") code: String, KtLintRuleEngine( ruleProviders = setOf(RuleProvider { object : Rule("test:astnode-utils-test") { - override fun visit( + override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: EmitType diff --git a/diktat-ruleset/build.gradle.kts b/diktat-ruleset/build.gradle.kts index e5f043e33a..407f672647 100644 --- a/diktat-ruleset/build.gradle.kts +++ b/diktat-ruleset/build.gradle.kts @@ -12,13 +12,15 @@ plugins { project.description = "This module builds jar that can be used to run diktat using ktlint -R via command line" dependencies { - api(projects.diktatRules) { - // Kotlin runtime & libraries will be provided by ktlint executable - exclude("org.jetbrains.kotlin", "kotlin-stdlib-common") - exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk8") - exclude("org.jetbrains.kotlin", "kotlin-stdlib") - exclude("org.jetbrains.kotlin", "kotlin-compiler-embeddable") - } + implementation(projects.diktatRules) + implementation(libs.log4j2.core) + implementation(libs.kotlinx.cli) + implementation(libs.ktlint.reporter.baseline) + implementation(libs.ktlint.reporter.checkstyle) + implementation(libs.ktlint.reporter.html) + implementation(libs.ktlint.reporter.json) + implementation(libs.ktlint.reporter.plain) + implementation(libs.ktlint.reporter.sarif) testImplementation(projects.diktatTestFramework) testImplementation(libs.kotlin.stdlib.common) testImplementation(libs.kotlin.stdlib.jdk7) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c875bd00ba..fd5cedddf3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,6 +28,10 @@ jupiter-itf-extension = "0.12.0" # FIXME: need to migrate to mockito mockito-all = "1.10.19" +# main class in executable jar +log4j2 = "2.20.0" +kotlinx-cli = "0.3.5" + # copied from save-cloud jetbrains-annotations = "24.0.1" @@ -41,8 +45,6 @@ diktat = "1.2.5" jgit = "6.5.0.202303070854-r" mockito = "5.2.0" mockito-kotlin = "4.1.0" -# only in save-cli -log4j = "2.20.0" testcontainers = "1.17.6" okhttp3 = "4.10.0" reckon = "0.16.1" @@ -50,7 +52,6 @@ commons-compress = "1.22" zip4j = "2.11.5" ktoml = "0.4.1" springdoc = "1.6.15" -kotlinx-cli = "0.3.5" spotless = "6.17.0" arrow-kt = "1.1.5" publish = "1.3.0" @@ -107,6 +108,7 @@ ktlint-reporter-sarif = { module = "com.pinterest.ktlint:ktlint-reporter-sarif", ktlint-reporter-json = { module = "com.pinterest.ktlint:ktlint-reporter-json", version.ref = "ktlint" } ktlint-reporter-html = { module = "com.pinterest.ktlint:ktlint-reporter-html", version.ref = "ktlint" } ktlint-reporter-baseline = { module = "com.pinterest.ktlint:ktlint-reporter-baseline", version.ref = "ktlint" } +ktlint-reporter-checkstyle = { module = "com.pinterest.ktlint:ktlint-reporter-checkstyle", version.ref = "ktlint" } sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version.ref = "sarif4k" } sarif4k-jvm = { module = "io.github.detekt.sarif4k:sarif4k-jvm", version.ref = "sarif4k" } @@ -120,6 +122,10 @@ jbool-expressions = { module = "com.bpodgursky:jbool_expressions", version.ref = # logging kotlin-logging = { module = "io.github.microutils:kotlin-logging", version.ref = "mu-logging" } +log4j2-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j2" } + +# cli +kotlinx-cli = { module = "org.jetbrains.kotlinx:kotlinx-cli", version.ref = "kotlinx-cli" } # testing junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } From a2e17aae1bf1252aeb75bf1c3259a245577c471f Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 30 Mar 2023 12:53:06 +0300 Subject: [PATCH 13/33] supported changes from master --- .../cqfn/diktat/plugin/maven/DiktatMojo.kt | 5 +- .../ktlint/KtLintRuleSetProviderV2Wrapper.kt | 53 +++++++++++++++++++ .../ktlint/KtLintRuleSetProviderWrapper.kt | 22 -------- .../diktat/ktlint/KtLintRuleSetWrapper.kt | 35 ------------ .../cqfn/diktat/ktlint/KtLintRuleWrapper.kt | 12 +++-- .../cqfn/diktat/ruleset/rules/DiktatRule.kt | 2 +- .../ruleset/rules/DiktatRuleSetFactory.kt | 40 -------------- .../ruleset/rules/DiktatRuleSetProviderSpi.kt | 17 ------ .../ruleset/rules/DiktatRuleSetProviderV2.kt | 34 ------------ .../rules/DiktatRuleSetProviderV2Spi.kt | 27 ++++++++++ ...om.pinterest.ktlint.core.RuleSetProviderV2 | 2 +- ...rapperTest.kt => KtLintRuleWrapperTest.kt} | 21 ++++---- .../smoke/RulesConfigValidationTest.kt | 3 +- .../diktat/util/DiktatRuleSetProvider4Test.kt | 15 +++--- .../main/kotlin/org/cqfn/diktat/DiktatMain.kt | 4 +- .../org/cqfn/diktat/DiktatProcessCommand.kt | 14 ++--- .../diktat/ruleset/smoke/DiktatSmokeTest.kt | 8 +-- 17 files changed, 123 insertions(+), 191 deletions(-) create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderWrapper.kt delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt rename diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/{KtLintRuleSetWrapperTest.kt => KtLintRuleWrapperTest.kt} (85%) diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt index 2f716a322f..a4747583c5 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt @@ -5,7 +5,6 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 import org.apache.maven.plugins.annotations.Mojo import kotlin.io.path.absolutePathString @@ -13,7 +12,7 @@ import kotlin.io.path.readText import kotlin.io.path.writeText /** - * Main [Mojo] that call [DiktatRuleSetProviderV2]'s rules on [inputs] files + * Main [Mojo] that call diktat's rules on [inputs] files */ @Mojo(name = "check") @Suppress("unused") @@ -27,7 +26,7 @@ class DiktatCheckMojo : DiktatBaseMojo() { } /** - * Main [Mojo] that call [DiktatRuleSetProviderV2]'s rules on [inputs] files + * Main [Mojo] that call diktat's rules on [inputs] files * and fixes discovered errors */ @Mojo(name = "fix") diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt new file mode 100644 index 0000000000..5d3a66fcba --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt @@ -0,0 +1,53 @@ +package org.cqfn.diktat.ktlint + +import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID +import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.asProvider +import org.cqfn.diktat.ruleset.rules.DiktatRule +import org.cqfn.diktat.ruleset.rules.DiktatRuleSet +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 + +/** + * This is a wrapper around __KtLint__'s [RuleSetProviderV2]. + */ +class KtLintRuleSetProviderV2Wrapper private constructor( + private val diktatRuleSetFactory: DiktatRuleSetProvider, +) : RuleSetProviderV2( + id = DIKTAT_RULE_SET_ID, + about = about, +) { + override fun getRuleProviders(): Set = diktatRuleSetFactory().toKtLint() + + companion object { + private fun Sequence.wrapRules(): Sequence = runningFold(null as KtLintRuleWrapper?) { prevRule, diktatRule -> + KtLintRuleWrapper(diktatRule, prevRule) + }.filterNotNull() + + /** + * About for diktat ruleset + */ + val about: About = About( + maintainer = "Diktat", + description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", + license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", + repositoryUrl = "https://github.com/saveourtool/diktat", + issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", + ) + + /** + * @return __KtLint__'s [RuleSetProviderV2] created from [DiktatRuleSetProvider] + */ + fun DiktatRuleSetProvider.toKtLint(): RuleSetProviderV2 = KtLintRuleSetProviderV2Wrapper(this) + + /** + * @return a set of __KtLint__'s [RuleProvider] created from [DiktatRuleSet] + */ + fun DiktatRuleSet.toKtLint(): Set = rules.asSequence() + .wrapRules() + .map { it.asProvider() } + .toSet() + } + +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderWrapper.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderWrapper.kt deleted file mode 100644 index 75a42e1272..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderWrapper.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import com.pinterest.ktlint.core.RuleSet -import com.pinterest.ktlint.core.RuleSetProvider - -/** - * This is a wrapper around __KtLint__'s [RuleSetProvider]. - */ -class KtLintRuleSetProviderWrapper private constructor( - private val diktatRuleSetFactory: DiktatRuleSetProvider, -) : RuleSetProvider { - override fun get(): RuleSet = diktatRuleSetFactory().toKtLint() - - companion object { - /** - * @return __KtLint__'s [RuleSetProvider] created from [DiktatRuleSetProvider] - */ - fun DiktatRuleSetProvider.toKtLint(): RuleSetProvider = KtLintRuleSetProviderWrapper(this) - } -} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt deleted file mode 100644 index e27fdaf978..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID -import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.ruleset.rules.DiktatRuleSet -import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSet - -/** - * This is a wrapper around __KtLint__'s [RuleSet] which adjusts visitorModifiers for all rules to keep order with prevRule - * Added as a workaround after introducing a new logic for sorting KtLint Rules: https://github.com/pinterest/ktlint/issues/1478 - * - * @param diktatRuleSet the rules which belong to the current [DiktatRuleSet]. - */ -class KtLintRuleSetWrapper private constructor( - diktatRuleSet: DiktatRuleSet, -) : RuleSet(DIKTAT_RULE_SET_ID, rules = wrapRules(diktatRuleSet.rules)) { - companion object { - /** - * @return __KtLint__'s [RuleSet] created from [DiktatRuleSet] - */ - fun DiktatRuleSet.toKtLint(): RuleSet = KtLintRuleSetWrapper(this) - - private fun wrapRules(rules: List): Array { - if (rules.isEmpty()) { - return emptyArray() - } - return rules.runningFold(null as KtLintRuleWrapper?) { prevRule, diktatRule -> - KtLintRuleWrapper(diktatRule, prevRule) - } - .filterNotNull() - .toTypedArray() - } - } -} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt index 97b905c749..a21d0fec38 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt @@ -5,6 +5,7 @@ import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId import org.cqfn.diktat.ruleset.constants.EmitType import org.cqfn.diktat.ruleset.rules.DiktatRule import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.RuleProvider import org.jetbrains.kotlin.com.intellij.lang.ASTNode /** @@ -18,11 +19,7 @@ class KtLintRuleWrapper( id = rule.id.qualifiedWithRuleSetId(DIKTAT_RULE_SET_ID), visitorModifiers = createVisitorModifiers(rule, prevRule), ) { - @Deprecated( - "Marked for deletion in ktlint 0.48.0", - replaceWith = ReplaceWith("beforeVisitChildNodes(node, autoCorrect, emit)"), - ) - override fun visit( + override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: EmitType, @@ -51,5 +48,10 @@ class KtLintRuleWrapper( * @return a rule to which a logic is delegated */ internal fun Rule.delegatee(): DiktatRule = (this as? KtLintRuleWrapper)?.rule ?: error("Provided rule ${javaClass.simpleName} is not wrapped by diktat") + + /** + * @return wraps [Rule] to [RuleProvider] + */ + internal fun Rule.asProvider(): RuleProvider = RuleProvider { this } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt index 04af8ea566..2ef540c1a4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRule.kt @@ -52,7 +52,7 @@ abstract class DiktatRule( * @throws Error */ @Suppress("TooGenericExceptionThrown") - fun beforeVisitChildNodes( + fun visit( node: ASTNode, autoCorrect: Boolean, emit: EmitType diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt deleted file mode 100644 index f28c1c4c13..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactory.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.cqfn.diktat.ruleset.rules - -import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF -import com.pinterest.ktlint.core.KtLintRuleEngine -import com.pinterest.ktlint.core.Rule -import kotlin.Function0 - -/** - * _KtLint_-agnostic factory which creates a [DiktatRuleSet]. - */ -fun interface DiktatRuleSetFactory : Function0 { - /** - * This method is going to be called once for each file (which means if any - * of the rules have state or are not thread-safe - a new [DiktatRuleSet] must - * be created). - * - * For each invocation of [KtLintRuleEngine.lint] and [KtLintRuleEngine.format] the [DiktatRuleSet] - * is retrieved. - * This results in new instances of each [Rule] for each file being - * processed. - * As of that a [Rule] does not need to be thread-safe. - * - * However, [KtLintRuleEngine.format] requires the [Rule] to be executed twice on a - * file in case at least one violation has been autocorrected. - * As the same [Rule] instance is reused for the second execution of the - * [Rule], the state of the [Rule] is shared. - * As of this [Rule] have to clear their internal state. - */ - override fun invoke(): DiktatRuleSet - - companion object { - /** - * @param diktatConfigFile the configuration file where all configurations for - * inspections and rules are stored. - * @return a new instance of [DiktatRuleSetFactory]. - */ - operator fun invoke(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): DiktatRuleSetFactory = - DiktatRuleSetProvider(diktatConfigFile) - } -} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt deleted file mode 100644 index 1bfcab8f82..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.cqfn.diktat.ruleset.rules - -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderWrapper.Companion.toKtLint -import com.pinterest.ktlint.core.RuleSet -import com.pinterest.ktlint.core.RuleSetProvider - -/** - * [RuleSetProvider] that provides diKTat ruleset. - * - * By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run - * otherwise it will use default configuration where some rules are disabled. - * - * This class is registered in [resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider] - */ -class DiktatRuleSetProviderSpi : RuleSetProvider { - override fun get(): RuleSet = DiktatRuleSetProvider().toKtLint().get() -} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt deleted file mode 100644 index c8befe4b84..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.cqfn.diktat.ruleset.rules - -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID -import com.pinterest.ktlint.core.RuleProvider -import com.pinterest.ktlint.core.RuleSetProviderV2 - -/** - * [RuleSetProviderV2] that provides diKTat ruleset. - * - * The no-argument constructor is used by the Java SPI interface; that's why - * it's explicitly annotated with [JvmOverloads]. - */ -@Suppress("serial") -class DiktatRuleSetProviderV2 -@JvmOverloads -constructor(private val factory: DiktatRuleSetFactory = DiktatRuleSetFactory()) : RuleSetProviderV2( - id = DIKTAT_RULE_SET_ID, - about = About( - maintainer = "Diktat", - description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", - license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", - repositoryUrl = "https://github.com/saveourtool/diktat", - issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", - ), -) { - /** - * @param diktatConfigFile the configuration file where all configurations for - * inspections and rules are stored. - */ - constructor(diktatConfigFile: String) : this(DiktatRuleSetFactory(diktatConfigFile)) - - override fun getRuleProviders(): Set = - factory().ruleProviders -} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt new file mode 100644 index 0000000000..0bef0b6748 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt @@ -0,0 +1,27 @@ +package org.cqfn.diktat.ruleset.rules + +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint +import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 + +/** + * [RuleSetProviderV2] that provides diKTat ruleset. + * + * By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run + * otherwise it will use default configuration where some rules are disabled. + * + * This class is registered in [resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2] + */ +class DiktatRuleSetProviderV2Spi private constructor( + private val ruleSetProviderV2: RuleSetProviderV2 +) : RuleSetProviderV2( + id = ruleSetProviderV2.id, + about = ruleSetProviderV2.about, +) { + /** + * The no-argument constructor is used by the Java SPI interface. + */ + constructor() : this(DiktatRuleSetProvider().toKtLint()) + + override fun getRuleProviders(): Set = ruleSetProviderV2.getRuleProviders() +} diff --git a/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 b/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 index 9794d26cc2..143ed2e22b 100644 --- a/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 +++ b/diktat-rules/src/main/resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2 @@ -1 +1 @@ -org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 +org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2Spi diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt similarity index 85% rename from diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt rename to diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt index 04bdb84c7a..51a08848aa 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt @@ -1,11 +1,10 @@ package org.cqfn.diktat.ktlint import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.delegatee import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.ruleset.rules.DiktatRuleSet -import org.cqfn.diktat.util.TEST_FILE_NAME import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.Rule @@ -16,7 +15,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -class KtLintRuleSetWrapperTest { +class KtLintRuleWrapperTest { @Test fun `check KtLintRuleSetWrapper with duplicate`() { val rule = mockRule("rule") @@ -32,12 +31,12 @@ class KtLintRuleSetWrapperTest { val rule1 = mockRule(id = "rule-first".qualifiedWithRuleSetId(ruleSetId)) val rule2 = mockRule(id = "rule-second".qualifiedWithRuleSetId(ruleSetId)) - val orderedRuleSet = DiktatRuleSet(listOf(rule1, rule2)).toKtLint() + val orderedRuleProviders = DiktatRuleSet(listOf(rule1, rule2)).toKtLint() - val orderedRuleSetIterator = orderedRuleSet.ruleProviders.iterator() - val orderedRule1 = orderedRuleSetIterator.next().createNewRuleInstance() - val orderedRule2 = orderedRuleSetIterator.next().createNewRuleInstance() - Assertions.assertFalse(orderedRuleSetIterator.hasNext(), "Extra elements after ordering") + val orderedRuleProviderIterator = orderedRuleProviders.iterator() + val orderedRule1 = orderedRuleProviderIterator.next().createNewRuleInstance() + val orderedRule2 = orderedRuleProviderIterator.next().createNewRuleInstance() + Assertions.assertFalse(orderedRuleProviderIterator.hasNext(), "Extra elements after ordering") Assertions.assertEquals(rule1, orderedRule1.delegatee(), "First rule is modified") @@ -80,14 +79,14 @@ class KtLintRuleSetWrapperTest { /* * Make sure OrderedRuleSet preserves the order. */ - val ruleSet = DiktatRuleSet(rules).toKtLint() - assertThat(ruleSet.ruleProviders.map(RuleProvider::createNewRuleInstance).map(Rule::id)).containsExactlyElementsOf(rules.map(Rule::id)) + val ruleProviders = DiktatRuleSet(rules).toKtLint() + assertThat(ruleProviders.map(RuleProvider::createNewRuleInstance).map(Rule::id)).containsExactlyElementsOf(rules.map(DiktatRule::id)) @Language("kotlin") val code = "fun foo() { }" KtLintRuleEngine( - ruleProviders = ruleSet.ruleProviders + ruleProviders = ruleProviders ).lint( code = Code.CodeSnippet( content = code diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt index 095757e3e8..755fa5e3ab 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt @@ -1,7 +1,6 @@ package org.cqfn.diktat.ruleset.smoke -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderWrapper.Companion.toKtLint -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently import com.charleskorn.kaml.InvalidPropertyValueException diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt index e9d027b0b4..bb526ac40e 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt @@ -4,12 +4,10 @@ package org.cqfn.diktat.util +import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.RulesConfigReader -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 -import org.cqfn.diktat.ruleset.rules.OrderedRuleSet.Companion.delegatee -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderWrapper.Companion.toKtLint -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.delegatee import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.ruleset.rules.DiktatRuleSet @@ -34,8 +32,10 @@ import kotlin.io.path.walk * Simple class for emulating [RuleSetProviderV2] to inject `.yml` rule configuration and mock this part of code. */ @Suppress("serial") -class DiktatRuleSetProvider4Test(private val ruleSupplier: (rulesConfigList: List) -> DiktatRule, - rulesConfigList: List?) : RuleSetProviderV2( +class DiktatRuleSetProvider4Test( + private val ruleSupplier: (rulesConfigList: List) -> DiktatRule, + rulesConfigList: List?, +) : RuleSetProviderV2( id = DIKTAT_RULE_SET_ID, about = NO_ABOUT, ) { @@ -59,7 +59,8 @@ class DiktatRuleSetProviderTest { .toList() val ruleNames = DiktatRuleSetProvider() .toKtLint() - .get() + .getRuleProviders() + .map { it.createNewRuleInstance() } .asSequence() .onEachIndexed { index, rule -> if (index != 0) { diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt index 506d257dc7..ed6ff788d0 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt @@ -9,7 +9,7 @@ import org.cqfn.diktat.api.DiktatMode import org.cqfn.diktat.cli.DiktatProperties import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactory +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import org.cqfn.diktat.util.tryToPathIfExists import org.cqfn.diktat.util.walkByGlob @@ -34,7 +34,7 @@ fun main(args: Array) { log.debug { "Loading diktatRuleSet using config ${properties.config}" } - val diktatRuleSetFactory = DiktatRuleSetFactory(properties.config) + val diktatRuleSetFactory = DiktatRuleSetProvider(properties.config) val reporter = properties.reporter() reporter.beforeAll() diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index 8451ee4ff0..0082e6836b 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -2,10 +2,10 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.ktlint.unwrap import org.cqfn.diktat.ktlint.unwrapForLint -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactory -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine import org.slf4j.Logger @@ -19,14 +19,14 @@ import java.nio.file.Path */ class DiktatProcessCommand private constructor( val file: Path, - diktatRuleSetFactory: DiktatRuleSetFactory, + diktatRuleSetFactory: DiktatRuleSetProvider, private val callback: DiktatCallback, ) { private val isDebug: Boolean by lazy { LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).isDebugEnabled } private val ktLintRuleEngine: KtLintRuleEngine = KtLintRuleEngine( - ruleProviders = DiktatRuleSetProviderV2(diktatRuleSetFactory).getRuleProviders(), + ruleProviders = diktatRuleSetFactory().toKtLint(), ) private val code: Code = Code.CodeFile( file = file.toFile() @@ -56,7 +56,7 @@ class DiktatProcessCommand private constructor( */ data class Builder( var file: Path? = null, - var diktatRuleSetFactory: DiktatRuleSetFactory? = null, + var diktatRuleSetFactory: DiktatRuleSetProvider? = null, var config: String? = null, var callback: DiktatCallback? = null, ) { @@ -70,7 +70,7 @@ class DiktatProcessCommand private constructor( * @param diktatRuleSetFactory * @return updated builder */ - fun diktatRuleSetFactory(diktatRuleSetFactory: DiktatRuleSetFactory) = require(config == null) { + fun diktatRuleSetFactory(diktatRuleSetFactory: DiktatRuleSetProvider) = require(config == null) { "diktatRuleSetFactory is set already via config" }.let { apply { this.diktatRuleSetFactory = diktatRuleSetFactory } @@ -99,7 +99,7 @@ class DiktatProcessCommand private constructor( requireNotNull(file) { "file is required" }, - diktatRuleSetFactory ?: DiktatRuleSetFactory(config ?: DIKTAT_ANALYSIS_CONF), + diktatRuleSetFactory ?: DiktatRuleSetProvider(config ?: DIKTAT_ANALYSIS_CONF), requireNotNull(callback) { "callback is required" }, diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt index c802fe5210..2d12fcc289 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -1,7 +1,7 @@ package org.cqfn.diktat.ruleset.smoke -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderWrapper.Companion.toKtLint -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProviderV2 +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.cqfn.diktat.ruleset.utils.format import org.cqfn.diktat.test.framework.processing.TestComparatorUnit import com.pinterest.ktlint.core.LintError @@ -11,7 +11,7 @@ import java.nio.file.Path import kotlin.io.path.absolutePathString /** - * Test for [DiktatRuleSetProviderV2] in autocorrect mode as a whole. All rules are applied to a file. + * Test for [DiktatRuleSetProvider] in autocorrect mode as a whole. All rules are applied to a file. * Note: ktlint uses initial text from a file to calculate line and column from offset. Because of that line/col of unfixed errors * may change after some changes to text or other rules. */ @@ -44,7 +44,7 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { resourceFilePath = RESOURCE_FILE_PATH, function = { expectedText, testFilePath -> format( - ruleSetProviderRef = { DiktatRuleSetProviderV2(config.absolutePathString()).toKtLint() }, + ruleSetProviderRef = { DiktatRuleSetProvider(config.absolutePathString()).toKtLint() }, text = expectedText, fileName = testFilePath, cb = { lintError, _ -> unfixedLintErrors.add(lintError) }, From f52f1ee2ee157952435294848b1bcf1e2c316ea9 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 30 Mar 2023 12:53:45 +0300 Subject: [PATCH 14/33] suppress unused --- .../org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt index 0bef0b6748..a1e13ba209 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt @@ -21,6 +21,7 @@ class DiktatRuleSetProviderV2Spi private constructor( /** * The no-argument constructor is used by the Java SPI interface. */ + @Suppress("unused") constructor() : this(DiktatRuleSetProvider().toKtLint()) override fun getRuleProviders(): Set = ruleSetProviderV2.getRuleProviders() From 0ab27fb046fd19fefbeef0e867578762240ac583 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 30 Mar 2023 13:37:34 +0300 Subject: [PATCH 15/33] supported changed after merge --- .../diktat/plugin/maven/DiktatBaseMojo.kt | 9 -- .../cqfn/diktat/plugin/maven/DiktatMojo.kt | 1 - .../cqfn/diktat/ruleset/utils/KtlintUtils.kt | 2 +- .../org/cqfn/diktat/DiktatProcessCommand.kt | 108 ------------------ .../org/cqfn/diktat/api/DiktatCallback.kt | 17 --- .../kotlin/org/cqfn/diktat/api/DiktatError.kt | 31 ----- .../diktat/ktlint/LintErrorCallbackWrapper.kt | 30 ----- .../cqfn/diktat/ktlint/LintErrorWrapper.kt | 34 ------ .../diktat/AbstractDiktatProcessCommand.kt | 26 ----- .../kotlin/org/cqfn/diktat/DiktatProcessor.kt | 2 +- .../kotlin/org/cqfn/diktat/api/DiktatMode.kt | 0 .../diktat-runner-cli/build.gradle.kts | 21 ++++ .../main/kotlin/org/cqfn/diktat/DiktatMain.kt | 9 +- .../org/cqfn/diktat/cli/DiktatProperties.kt | 4 +- .../kotlin/org/cqfn/diktat/util/CliUtils.kt | 38 ++++++ .../kotlin/org/cqfn/diktat/util/FileUtils.kt | 0 .../build.gradle.kts | 6 + .../org/cqfn/diktat/DiktatProcessCommand.kt | 50 +++----- .../diktat/ktlint/LintErrorCallbackWrapper.kt | 25 +++- .../org/cqfn/diktat/ktlint/ReporterUtil.kt | 72 +++--------- settings.gradle.kts | 1 + 21 files changed, 128 insertions(+), 358 deletions(-) delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt rename {diktat-ruleset => diktat-runner/diktat-runner-api}/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt (100%) create mode 100644 diktat-runner/diktat-runner-cli/build.gradle.kts rename {diktat-ruleset => diktat-runner/diktat-runner-cli}/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt (91%) rename {diktat-ruleset => diktat-runner/diktat-runner-cli}/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt (98%) create mode 100644 diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt rename {diktat-ruleset => diktat-runner/diktat-runner-cli}/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt (100%) rename {diktat-ruleset => diktat-runner/diktat-runner-ktlint-engine}/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt (63%) diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 72afeb7176..55490a048f 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -2,9 +2,7 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.DiktatProcessCommand import org.cqfn.diktat.DiktatProcessor -import org.cqfn.diktat.api.DiktatLogLevel import org.cqfn.diktat.ktlint.LintErrorReporter import org.cqfn.diktat.ktlint.unwrap import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript @@ -16,10 +14,6 @@ import com.pinterest.ktlint.core.api.KtLintParseException import com.pinterest.ktlint.core.api.KtLintRuleException import com.pinterest.ktlint.core.api.containsLintError import com.pinterest.ktlint.core.api.loadBaseline -import com.pinterest.ktlint.core.RuleExecutionException -import com.pinterest.ktlint.core.internal.CurrentBaseline -import com.pinterest.ktlint.core.internal.containsLintError -import com.pinterest.ktlint.core.internal.loadBaseline import com.pinterest.ktlint.reporter.baseline.BaselineReporter import com.pinterest.ktlint.reporter.html.HtmlReporter import com.pinterest.ktlint.reporter.json.JsonReporter @@ -123,9 +117,6 @@ abstract class DiktatBaseMojo : AbstractMojo() { val diktatProcessor by lazy { DiktatProcessor.builder() .diktatRuleSetProvider(configFile) - .logLevel( - if (debug) DiktatLogLevel.DEBUG else DiktatLogLevel.INFO - ) .build() } val baselineResults = baseline?.let { loadBaseline(it.absolutePath) } diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt index 7438aa76bf..fab0ea0819 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatMojo.kt @@ -5,7 +5,6 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand - import org.apache.maven.plugins.annotations.Mojo /** diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt index 9389f3bdd6..ed5be02e38 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt @@ -20,8 +20,8 @@ val defaultCallback: (lintError: LintError, corrected: Boolean) -> Unit = { lint log.warn("Received linting error: $lintError") } -typealias LintCallback = (LintError) -> Unit typealias FormatCallback = (LintError, Boolean) -> Unit +typealias LintCallback = (LintError) -> Unit /** * @param ruleSetProviderRef diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt deleted file mode 100644 index 0082e6836b..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.cqfn.diktat - -import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint -import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.ktlint.unwrapForLint -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import com.pinterest.ktlint.core.Code -import com.pinterest.ktlint.core.KtLintRuleEngine -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.nio.file.Path - -/** - * Command to run `diktat` - * - * @property file - */ -class DiktatProcessCommand private constructor( - val file: Path, - diktatRuleSetFactory: DiktatRuleSetProvider, - private val callback: DiktatCallback, -) { - private val isDebug: Boolean by lazy { - LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).isDebugEnabled - } - private val ktLintRuleEngine: KtLintRuleEngine = KtLintRuleEngine( - ruleProviders = diktatRuleSetFactory().toKtLint(), - ) - private val code: Code = Code.CodeFile( - file = file.toFile() - ) - - /** - * Run `diktat fix` using parameters from current command - * - * @return result of `diktat fix` - */ - fun fix(): String = ktLintRuleEngine.format(code, callback.unwrap()) - - /** - * Run `diktat check` using parameters from current command - */ - fun check() { - ktLintRuleEngine.lint(code, callback.unwrapForLint()) - } - - /** - * Builder for [DiktatProcessCommand] - * - * @property file - * @property diktatRuleSetFactory - * @property config - * @property callback - */ - data class Builder( - var file: Path? = null, - var diktatRuleSetFactory: DiktatRuleSetProvider? = null, - var config: String? = null, - var callback: DiktatCallback? = null, - ) { - /** - * @param file - * @return updated builder - */ - fun file(file: Path) = apply { this.file = file } - - /** - * @param diktatRuleSetFactory - * @return updated builder - */ - fun diktatRuleSetFactory(diktatRuleSetFactory: DiktatRuleSetProvider) = require(config == null) { - "diktatRuleSetFactory is set already via config" - }.let { - apply { this.diktatRuleSetFactory = diktatRuleSetFactory } - } - - /** - * @param config - * @return updated builder - */ - fun config(config: String) = require(diktatRuleSetFactory == null) { - "diktatRuleSetFactory is already provided directly" - }.let { - apply { this.config = config } - } - - /** - * @param callback - * @return updated builder - */ - fun callback(callback: DiktatCallback) = apply { this.callback = callback } - - /** - * @return built [DiktatProcessCommand] - */ - fun build() = DiktatProcessCommand( - requireNotNull(file) { - "file is required" - }, - diktatRuleSetFactory ?: DiktatRuleSetProvider(config ?: DIKTAT_ANALYSIS_CONF), - requireNotNull(callback) { - "callback is required" - }, - ) - } -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt deleted file mode 100644 index ba4141c9c8..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatCallback.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.cqfn.diktat.api - -import java.util.function.BiConsumer - -/** - * Callback for diktat process - */ -@FunctionalInterface -fun interface DiktatCallback : BiConsumer { - /** - * Performs this callback on the given [error] taking into account [isCorrected] flag. - * - * @param error the error found by diktat - * @param isCorrected true if the error fixed by diktat - */ - override fun accept(error: DiktatError, isCorrected: Boolean) -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt deleted file mode 100644 index 3d4aac4758..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatError.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.cqfn.diktat.api - -/** - * Error found by `diktat` - */ -interface DiktatError { - /** - * @return line number (one-based) - */ - fun getLine(): Int - - /** - * @return column number (one-based) - */ - fun getCol(): Int - - /** - * @return rule id - */ - fun getRuleId(): String - - /** - * @return error message - */ - fun getDetail(): String - - /** - * @return true if the found error can be fixed - */ - fun canBeAutoCorrected(): Boolean -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt deleted file mode 100644 index 7d784d6a76..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt +++ /dev/null @@ -1,30 +0,0 @@ -/** - * This file contains utility methods for LintErrorCallback - */ - -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.ruleset.utils.FormatCallback -import org.cqfn.diktat.ruleset.utils.LintCallback - -/** - * @return [DiktatCallback] from KtLint [FormatCallback] - */ -fun FormatCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> - this(error.unwrap(), isCorrected) -} - -/** - * @return KtLint [FormatCallback] from [DiktatCallback] or exception - */ -fun DiktatCallback.unwrap(): FormatCallback = { error, isCorrected -> - this.accept(error.wrap(), isCorrected) -} - -/** - * @return KtLint [FormatCallback] from [DiktatCallback] or exception - */ -fun DiktatCallback.unwrapForLint(): LintCallback = { error -> - this.accept(error.wrap(), false) -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt deleted file mode 100644 index b293a1cee5..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorWrapper.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.api.DiktatError -import com.pinterest.ktlint.core.LintError - -/** - * Wrapper for KtLint error - * - * @property lintError - */ -data class LintErrorWrapper( - val lintError: LintError -) : DiktatError { - override fun getLine(): Int = lintError.line - - override fun getCol(): Int = lintError.col - - override fun getRuleId(): String = lintError.ruleId - - override fun getDetail(): String = lintError.detail - - override fun canBeAutoCorrected(): Boolean = lintError.canBeAutoCorrected -} - -/** - * @return [DiktatError] from KtLint [LintError] - */ -fun LintError.wrap(): DiktatError = LintErrorWrapper(this) - -/** - * @return KtLint [LintError] from [DiktatError] or exception - */ -fun DiktatError.unwrap(): LintError = (this as? LintErrorWrapper)?.lintError - ?: error("Unsupported wrapper of ${DiktatError::class.java.simpleName}: ${this::class.java.canonicalName}") diff --git a/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/AbstractDiktatProcessCommand.kt b/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/AbstractDiktatProcessCommand.kt index 98b6c05338..03f2562f0c 100644 --- a/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/AbstractDiktatProcessCommand.kt +++ b/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/AbstractDiktatProcessCommand.kt @@ -1,24 +1,18 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.ruleset.utils.isKotlinScript import java.nio.file.Path -import kotlin.io.path.readText /** * An abstract implementation for command to run `diktat` * * @property processor * @property file - * @property fileContent - * @property isScript * @property callback */ abstract class AbstractDiktatProcessCommand( protected val processor: DiktatProcessor, protected val file: Path, - protected val fileContent: String, - protected val isScript: Boolean, protected val callback: DiktatCallback, ) { /** @@ -39,8 +33,6 @@ abstract class AbstractDiktatProcessCommand( abstract class Builder { private var processor: DiktatProcessor? = null private var file: Path? = null - private var fileContent: String? = null - private var isScript: Boolean? = null private var callback: DiktatCallback? = null /** @@ -55,18 +47,6 @@ abstract class AbstractDiktatProcessCommand( */ fun file(file: Path) = apply { this.file = file } - /** - * @param fileContent - * @return updated builder - */ - fun fileContent(fileContent: String) = apply { this.fileContent = fileContent } - - /** - * @param isScript - * @return updated builder - */ - fun isScript(isScript: Boolean) = apply { this.isScript = isScript } - /** * @param callback * @return updated builder @@ -85,8 +65,6 @@ abstract class AbstractDiktatProcessCommand( "processor is required" }, file = resolvedFile, - fileContent = fileContent ?: resolvedFile.readText(Charsets.UTF_8), - isScript = isScript ?: resolvedFile.isKotlinScript(), callback = requireNotNull(callback) { "callback is required" }, @@ -96,16 +74,12 @@ abstract class AbstractDiktatProcessCommand( /** * @param processor * @param file - * @param fileContent - * @param isScript * @param callback * @return [C] is built using values from builder */ abstract fun doBuild( processor: DiktatProcessor, file: Path, - fileContent: String, - isScript: Boolean, callback: DiktatCallback, ): C } diff --git a/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt b/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt index 69475f996f..8349cb3944 100644 --- a/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt +++ b/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt @@ -17,7 +17,7 @@ class DiktatProcessor private constructor( val logLevel: DiktatLogLevel, ) { /** - * Builder for [DiktatProcessCommand] + * Builder for [DiktatProcessor] * * @property diktatRuleSetProvider * @property logLevel diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt b/diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt similarity index 100% rename from diktat-ruleset/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt rename to diktat-runner/diktat-runner-api/src/main/kotlin/org/cqfn/diktat/api/DiktatMode.kt diff --git a/diktat-runner/diktat-runner-cli/build.gradle.kts b/diktat-runner/diktat-runner-cli/build.gradle.kts new file mode 100644 index 0000000000..12d376f8dc --- /dev/null +++ b/diktat-runner/diktat-runner-cli/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("org.cqfn.diktat.buildutils.kotlin-jvm-configuration") + id("org.cqfn.diktat.buildutils.code-quality-convention") + id("org.cqfn.diktat.buildutils.publishing-signing-default-configuration") +} + +project.description = "This module builds diktat-runner implementation using ktlint as CLI" + +dependencies { + api(projects.diktatRunner.diktatRunnerApi) + implementation(projects.diktatRules) + implementation(projects.diktatRunner.diktatRunnerKtlintEngine) + implementation(libs.kotlinx.cli) + implementation(libs.log4j2.core) + implementation(libs.ktlint.reporter.baseline) + implementation(libs.ktlint.reporter.checkstyle) + implementation(libs.ktlint.reporter.html) + implementation(libs.ktlint.reporter.json) + implementation(libs.ktlint.reporter.plain) + implementation(libs.ktlint.reporter.sarif) +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt similarity index 91% rename from diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt rename to diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt index ed6ff788d0..5c87f8dd0d 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt +++ b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt @@ -9,7 +9,6 @@ import org.cqfn.diktat.api.DiktatMode import org.cqfn.diktat.cli.DiktatProperties import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import org.cqfn.diktat.util.tryToPathIfExists import org.cqfn.diktat.util.walkByGlob @@ -34,7 +33,9 @@ fun main(args: Array) { log.debug { "Loading diktatRuleSet using config ${properties.config}" } - val diktatRuleSetFactory = DiktatRuleSetProvider(properties.config) + val diktatProcessor = DiktatProcessor.builder() + .diktatRuleSetProvider(properties.config) + .build() val reporter = properties.reporter() reporter.beforeAll() @@ -56,9 +57,9 @@ fun main(args: Array) { "Start processing the file: $file" } val result: MutableList = mutableListOf() - DiktatProcessCommand.Builder() + DiktatProcessCommand.builder() + .processor(diktatProcessor) .file(file) - .diktatRuleSetFactory(diktatRuleSetFactory) .callback { error, isCorrected -> result.add(error to isCorrected) } diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt similarity index 98% rename from diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt rename to diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt index a03c0960c4..8f289f702e 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt +++ b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt @@ -4,8 +4,8 @@ import org.cqfn.diktat.api.DiktatMode import org.cqfn.diktat.common.config.rules.DIKTAT import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF import org.cqfn.diktat.ktlint.buildReporter -import org.cqfn.diktat.ktlint.colorName -import org.cqfn.diktat.ktlint.reporterProviderId +import org.cqfn.diktat.util.colorName +import org.cqfn.diktat.util.reporterProviderId import com.pinterest.ktlint.core.Reporter import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.core.LoggerContext diff --git a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt new file mode 100644 index 0000000000..1f4c18a7a7 --- /dev/null +++ b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt @@ -0,0 +1,38 @@ +package org.cqfn.diktat.util + +import org.cqfn.diktat.ktlint.colorNamesForPlainReporter +import org.cqfn.diktat.ktlint.plainReporterProvider +import org.cqfn.diktat.ktlint.reporterProviders +import com.pinterest.ktlint.core.ReporterProvider +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default + +/** + * @return a single [ReporterProvider] as parsed cli arg + */ +internal fun ArgParser.reporterProviderId() = option( + type = ArgType.Choice( + choices = reporterProviders.keys.toList(), + toVariant = { it }, + variantToString = { it }, + ), + fullName = "reporter", + shortName = "r", + description = "The reporter to use" +) + .default(plainReporterProvider.id) + +/** + * @return a single and optional color name as parsed cli args + */ +internal fun ArgParser.colorName() = this.option( + type = ArgType.Choice( + choices = colorNamesForPlainReporter, + toVariant = { it }, + variantToString = { it }, + ), + fullName = "plain-color", + shortName = null, + description = "Colorize the output.", +) diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt similarity index 100% rename from diktat-ruleset/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt rename to diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt diff --git a/diktat-runner/diktat-runner-ktlint-engine/build.gradle.kts b/diktat-runner/diktat-runner-ktlint-engine/build.gradle.kts index 9e3534faa5..a32060b715 100644 --- a/diktat-runner/diktat-runner-ktlint-engine/build.gradle.kts +++ b/diktat-runner/diktat-runner-ktlint-engine/build.gradle.kts @@ -10,4 +10,10 @@ dependencies { api(projects.diktatRunner.diktatRunnerApi) implementation(projects.diktatRules) implementation(libs.ktlint.core) + implementation(libs.ktlint.reporter.baseline) + implementation(libs.ktlint.reporter.checkstyle) + implementation(libs.ktlint.reporter.html) + implementation(libs.ktlint.reporter.json) + implementation(libs.ktlint.reporter.plain) + implementation(libs.ktlint.reporter.sarif) } diff --git a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index 04a605af5d..e09d2db0fd 100644 --- a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -1,61 +1,51 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.api.DiktatLogLevel -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderWrapper.Companion.toKtLint -import org.cqfn.diktat.ktlint.unwrap -import com.pinterest.ktlint.core.KtLint -import com.pinterest.ktlint.core.api.EditorConfigOverride +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint +import org.cqfn.diktat.ktlint.unwrapForLint +import org.cqfn.diktat.ktlint.unwrapForFormat +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import java.nio.file.Path -import kotlin.io.path.absolutePathString /** * Command to run `diktat` * * @property processor * @property file - * @property fileContent - * @property isScript * @property callback */ class DiktatProcessCommand private constructor( processor: DiktatProcessor, file: Path, - fileContent: String, - isScript: Boolean, callback: DiktatCallback, ) : AbstractDiktatProcessCommand( processor, file, - fileContent, - isScript, callback, ) { + private val ktLintRuleEngine: KtLintRuleEngine by lazy { + KtLintRuleEngine( + ruleProviders = processor.diktatRuleSetProvider.toKtLint().getRuleProviders(), + ) + } + private val code: Code by lazy { + Code.CodeFile( + file = file.toFile() + ) + } + /** * Run `diktat fix` using parameters from current command * * @return result of `diktat fix` */ - override fun fix(): String = KtLint.format(ktLintParams()) + override fun fix(): String = ktLintRuleEngine.format(code, callback.unwrapForFormat()) /** * Run `diktat check` using parameters from current command */ - override fun check(): Unit = KtLint.lint(ktLintParams()) - - @Suppress("DEPRECATION") - private fun ktLintParams(): KtLint.ExperimentalParams = KtLint.ExperimentalParams( - fileName = file.absolutePathString(), - text = fileContent, - ruleSets = setOf(processor.diktatRuleSetProvider.toKtLint().get()), - userData = emptyMap(), - cb = callback.unwrap(), - script = isScript, - editorConfigPath = null, - debug = processor.logLevel == DiktatLogLevel.DEBUG, - editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride, - isInvokedFromCli = false - ) + override fun check(): Unit = ktLintRuleEngine.lint(code, callback.unwrapForLint()) /** * Builder for [DiktatProcessCommand] @@ -64,14 +54,10 @@ class DiktatProcessCommand private constructor( override fun doBuild( processor: DiktatProcessor, file: Path, - fileContent: String, - isScript: Boolean, callback: DiktatCallback, ): DiktatProcessCommand = DiktatProcessCommand( processor = processor, file = file, - fileContent = fileContent, - isScript = isScript, callback = callback, ) } diff --git a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt index b9352d2a57..30ddd708e7 100644 --- a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt +++ b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/LintErrorCallbackWrapper.kt @@ -5,18 +5,33 @@ package org.cqfn.diktat.ktlint import org.cqfn.diktat.api.DiktatCallback -import org.cqfn.diktat.ruleset.utils.LintErrorCallback +import org.cqfn.diktat.ruleset.utils.FormatCallback +import org.cqfn.diktat.ruleset.utils.LintCallback /** - * @return [DiktatCallback] from KtLint [LintErrorCallback] + * @return [DiktatCallback] from KtLint [FormatCallback] */ -fun LintErrorCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> +fun FormatCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> this(error.unwrap(), isCorrected) } /** - * @return KtLint [LintErrorCallback] from [DiktatCallback] or exception + * @return [DiktatCallback] from KtLint [LintCallback] */ -fun DiktatCallback.unwrap(): LintErrorCallback = { error, isCorrected -> +fun LintCallback.wrap(): DiktatCallback = DiktatCallback { error, _ -> + this(error.unwrap()) +} + +/** + * @return KtLint [FormatCallback] from [DiktatCallback] or exception + */ +fun DiktatCallback.unwrapForFormat(): FormatCallback = { error, isCorrected -> this(error.wrap(), isCorrected) } + +/** + * @return KtLint [LintCallback] from [DiktatCallback] or exception + */ +fun DiktatCallback.unwrapForLint(): LintCallback = { error -> + this(error.wrap(), false) +} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt similarity index 63% rename from diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt rename to diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt index 6cc546638b..a9717ad304 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt +++ b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt @@ -10,21 +10,28 @@ import com.pinterest.ktlint.core.ReporterProvider import com.pinterest.ktlint.reporter.checkstyle.CheckStyleReporterProvider import com.pinterest.ktlint.reporter.html.HtmlReporterProvider import com.pinterest.ktlint.reporter.json.JsonReporterProvider +import com.pinterest.ktlint.reporter.plain.Color import com.pinterest.ktlint.reporter.plain.PlainReporterProvider import com.pinterest.ktlint.reporter.sarif.SarifReporterProvider import java.io.PrintStream import java.nio.file.Paths import kotlin.io.path.createDirectories import kotlin.io.path.outputStream -import kotlinx.cli.ArgParser -import kotlinx.cli.ArgType -import kotlinx.cli.default -private const val DEFAULT_COLOR_NAME = "DARK_GRAY" +/** + * supported color names in __KtLint__, taken from [Color] + */ +val colorNamesForPlainReporter = Color.values().map { it.name } -private val plainReporterProvider = PlainReporterProvider() +/** + * A default [ReporterProvider] for [PlainReporterProvider] + */ +val plainReporterProvider = PlainReporterProvider() -private val reporterProviders = setOf( +/** + * All [ReporterProvider] which __KtLint__ provides + */ +val reporterProviders = setOf( plainReporterProvider, JsonReporterProvider(), SarifReporterProvider(), @@ -33,55 +40,6 @@ private val reporterProviders = setOf( ) .associateBy { it.id } -// supported color names in KtLint -private val colorNames = listOf( - "BLACK", - "RED", - "GREEN", - "YELLOW", - "BLUE", - "MAGENTA", - "CYAN", - "LIGHT_GRAY", - DEFAULT_COLOR_NAME, - "LIGHT_RED", - "LIGHT_GREEN", - "LIGHT_YELLOW", - "LIGHT_BLUE", - "LIGHT_MAGENTA", - "LIGHT_CYAN", - "WHITE", -) - -/** - * @return a single [ReporterProvider] as parsed cli arg - */ -internal fun ArgParser.reporterProviderId() = option( - type = ArgType.Choice( - choices = reporterProviders.keys.toList(), - toVariant = { it }, - variantToString = { it }, - ), - fullName = "reporter", - shortName = "r", - description = "The reporter to use" -) - .default(plainReporterProvider.id) - -/** - * @return a single and optional color name as parsed cli args - */ -internal fun ArgParser.colorName() = this.option( - type = ArgType.Choice( - choices = colorNames, - toVariant = { it }, - variantToString = { it }, - ), - fullName = "plain-color", - shortName = null, - description = "Colorize the output.", -) - /** * @return true if receiver is [PlainReporterProvider] */ @@ -95,7 +53,7 @@ internal fun ReporterProvider<*>.isPlain(): Boolean = id == plainReporterProvide * @param mode * @return a configured [Reporter] */ -internal fun buildReporter( +fun buildReporter( reporterProviderId: String, output: String?, colorNameInPlain: String?, @@ -119,7 +77,7 @@ internal fun buildReporter( put("color_name", it) } ?: run { put("color", false) - put("color_name", DEFAULT_COLOR_NAME) + put("color_name", Color.DARK_GRAY.name) } put("format", (mode == DiktatMode.FIX)) if (groupByFileInPlain) { diff --git a/settings.gradle.kts b/settings.gradle.kts index b9c909cb82..9f6c6db5ed 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,7 @@ include("diktat-ruleset") include("diktat-test-framework") include("diktat-dev-ksp") include("diktat-runner:diktat-runner-api") +include("diktat-runner:diktat-runner-cli") include("diktat-runner:diktat-runner-ktlint-engine") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") From c9e4bbc4310babf7dd6c9be3ffc9f7bb29eea03d Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 30 Mar 2023 13:43:41 +0300 Subject: [PATCH 16/33] diktatFix --- .../kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt | 3 ++- .../src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt | 4 ++++ .../src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 55490a048f..a93f81077e 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -1,10 +1,10 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatProcessCommand -import org.cqfn.diktat.ktlint.unwrap import org.cqfn.diktat.DiktatProcessor import org.cqfn.diktat.ktlint.LintErrorReporter import org.cqfn.diktat.ktlint.unwrap +import org.cqfn.diktat.ktlint.unwrap import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript import com.pinterest.ktlint.core.LintError @@ -31,6 +31,7 @@ import java.io.File import java.io.FileOutputStream import java.io.PrintStream import java.nio.file.Path + import kotlin.io.path.absolutePathString import kotlin.io.path.readText import kotlin.io.path.writeText diff --git a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt index 1f4c18a7a7..d1da8ec262 100644 --- a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt +++ b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt @@ -1,3 +1,7 @@ +/** + * This class contains util methods to operate with kotlinx.cli.* + */ + package org.cqfn.diktat.util import org.cqfn.diktat.ktlint.colorNamesForPlainReporter diff --git a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt index e09d2db0fd..aa7def0197 100644 --- a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt +++ b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/DiktatProcessCommand.kt @@ -2,10 +2,12 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint -import org.cqfn.diktat.ktlint.unwrapForLint import org.cqfn.diktat.ktlint.unwrapForFormat +import org.cqfn.diktat.ktlint.unwrapForLint + import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine + import java.nio.file.Path /** From 2ca20bdcb2b266f1434a612e1a21626264cf2bde Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 30 Mar 2023 13:58:41 +0300 Subject: [PATCH 17/33] diktatFix & detektAll --- .../plugin/gradle/tasks/DiktatTaskBase.kt | 34 +++++++++---------- .../diktat/plugin/maven/DiktatBaseMojo.kt | 12 +++---- .../ktlint/KtLintRuleSetProviderV2Wrapper.kt | 19 ++++------- .../ruleset/rules/DiktatRuleSetProvider.kt | 1 - 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/tasks/DiktatTaskBase.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/tasks/DiktatTaskBase.kt index 2a1328bcd7..8d229c4938 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/tasks/DiktatTaskBase.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/tasks/DiktatTaskBase.kt @@ -17,9 +17,9 @@ import org.cqfn.diktat.plugin.gradle.getReporterType import org.cqfn.diktat.plugin.gradle.isSarifReporterActive import com.pinterest.ktlint.core.Reporter -import com.pinterest.ktlint.core.internal.CurrentBaseline -import com.pinterest.ktlint.core.internal.containsLintError -import com.pinterest.ktlint.core.internal.loadBaseline +import com.pinterest.ktlint.core.api.Baseline +import com.pinterest.ktlint.core.api.containsLintError +import com.pinterest.ktlint.core.api.loadBaseline import com.pinterest.ktlint.reporter.baseline.BaselineReporter import com.pinterest.ktlint.reporter.html.HtmlReporter import com.pinterest.ktlint.reporter.json.JsonReporter @@ -101,9 +101,9 @@ abstract class DiktatTaskBase( * A baseline loaded from provided file or empty */ @get:Internal - internal val baseline: CurrentBaseline by lazy { + internal val baseline: Baseline by lazy { extension.baseline?.let { loadBaseline(it) } - ?: CurrentBaseline(emptyMap(), false) + ?: Baseline(status = Baseline.Status.NOT_FOUND) } /** @@ -164,9 +164,10 @@ abstract class DiktatTaskBase( ) { project.logger.debug("Checking file $file") reporter.before(file.absolutePath) - val baselineErrors = baseline.baselineRules?.get( - file.relativeTo(project.projectDir).invariantSeparatorsPath - ) ?: emptyList() + val baselineErrors = baseline.lintErrorsPerFile.getOrDefault( + file.relativeTo(project.projectDir).invariantSeparatorsPath, + emptyList() + ) val diktatCallback = DiktatCallback { error, isCorrected -> val ktLintError = error.unwrap() if (!baselineErrors.containsLintError(ktLintError)) { @@ -197,7 +198,7 @@ abstract class DiktatTaskBase( */ protected abstract fun doRun(diktatCommand: DiktatProcessCommand, formattedContentConsumer: (String) -> Unit) - private fun resolveReporter(baselineResults: CurrentBaseline): Reporter { + private fun resolveReporter(baselineResults: Baseline): Reporter { val reporterType = project.getReporterType(extension) if (isSarifReporterActive(reporterType)) { // need to set user.home specially for ktlint, so it will be able to put a relative path URI in SARIF @@ -219,14 +220,11 @@ abstract class DiktatTaskBase( } } - return if (baselineResults.baselineGenerationNeeded) { - val baseline = requireNotNull(extension.baseline) { - "baseline is not provided, but baselineGenerationNeeded is true" - } - val baselineReporter = BaselineReporter(PrintStream(FileOutputStream(baseline, true))) - return Reporter.from(actualReporter, baselineReporter) - } else { - actualReporter - } + return extension.baseline + ?.takeUnless { baselineResults.status == Baseline.Status.VALID } + ?.let { baseline -> + val baselineReporter = BaselineReporter(PrintStream(FileOutputStream(baseline, true))) + Reporter.from(actualReporter, baselineReporter) + } ?: actualReporter } } diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index a93f81077e..24df88ec05 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -166,12 +166,12 @@ abstract class DiktatBaseMojo : AbstractMojo() { } } - return if (baselineResults.status == Baseline.Status.NOT_FOUND) { - val baselineReporter = BaselineReporter(PrintStream(FileOutputStream(baseline, true))) - return Reporter.from(actualReporter, baselineReporter) - } else { - actualReporter - } + return baseline + ?.takeUnless { baselineResults.status == Baseline.Status.VALID } + ?.let { + val baselineReporter = BaselineReporter(PrintStream(FileOutputStream(it, true))) + Reporter.from(actualReporter, baselineReporter) + } ?: actualReporter } /** diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt index 5d3a66fcba..59c5b4e0e6 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt @@ -16,7 +16,13 @@ class KtLintRuleSetProviderV2Wrapper private constructor( private val diktatRuleSetFactory: DiktatRuleSetProvider, ) : RuleSetProviderV2( id = DIKTAT_RULE_SET_ID, - about = about, + about = About( + maintainer = "Diktat", + description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", + license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", + repositoryUrl = "https://github.com/saveourtool/diktat", + issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", + ), ) { override fun getRuleProviders(): Set = diktatRuleSetFactory().toKtLint() @@ -25,17 +31,6 @@ class KtLintRuleSetProviderV2Wrapper private constructor( KtLintRuleWrapper(diktatRule, prevRule) }.filterNotNull() - /** - * About for diktat ruleset - */ - val about: About = About( - maintainer = "Diktat", - description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", - license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", - repositoryUrl = "https://github.com/saveourtool/diktat", - issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", - ) - /** * @return __KtLint__'s [RuleSetProviderV2] created from [DiktatRuleSetProvider] */ diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt index b6425b686e..6d23ea8908 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt @@ -135,7 +135,6 @@ class DiktatRuleSetProvider(private val diktatConfigFile: String = DIKTAT_ANALYS * of the rules have state or are not thread-safe - a new [DiktatRuleSet] must * be created). * - * TODO: comments for 0.47.x * For each invocation of [com.pinterest.ktlint.core.KtLintRuleEngine.lint] and [com.pinterest.ktlint.core.KtLintRuleEngine.format] the [DiktatRuleSet] * is retrieved. * This results in new instances of each [Rule] for each file being From 3197715c9af6e02ca6894cb71e3c1500b350c9f6 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 30 Mar 2023 14:46:15 +0300 Subject: [PATCH 18/33] refactored tests --- ...r4Test.kt => DiktatRuleSetProviderTest.kt} | 41 ++++++++----------- .../org/cqfn/diktat/util/FixTestBase.kt | 24 ++++++++--- .../org/cqfn/diktat/util/LintTestBase.kt | 5 ++- .../kotlin/org/cqfn/diktat/util/TestUtils.kt | 33 +++++++++++++++ .../diktat/ruleset/smoke/DiktatSmokeTest.kt | 20 ++++++--- 5 files changed, 85 insertions(+), 38 deletions(-) rename diktat-rules/src/test/kotlin/org/cqfn/diktat/util/{DiktatRuleSetProvider4Test.kt => DiktatRuleSetProviderTest.kt} (72%) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt similarity index 72% rename from diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt rename to diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt index bb526ac40e..1976c1e834 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProvider4Test.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt @@ -1,10 +1,5 @@ -/** - * Stub for diktat ruleset provide to be used in tests and other related utilities - */ - package org.cqfn.diktat.util -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.RulesConfigReader import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint @@ -15,8 +10,6 @@ import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.cqfn.diktat.test.framework.util.filterContentMatches import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleProvider -import com.pinterest.ktlint.core.RuleSetProviderV2 import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -28,22 +21,6 @@ import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension import kotlin.io.path.walk -/** - * Simple class for emulating [RuleSetProviderV2] to inject `.yml` rule configuration and mock this part of code. - */ -@Suppress("serial") -class DiktatRuleSetProvider4Test( - private val ruleSupplier: (rulesConfigList: List) -> DiktatRule, - rulesConfigList: List?, -) : RuleSetProviderV2( - id = DIKTAT_RULE_SET_ID, - about = NO_ABOUT, -) { - private val rulesConfigList: List? = rulesConfigList ?: RulesConfigReader(javaClass.classLoader).readResource("diktat-analysis.yml") - - override fun getRuleProviders(): Set = DiktatRuleSet(listOf(ruleSupplier.invoke(rulesConfigList ?: emptyList()))).toKtLint() -} - class DiktatRuleSetProviderTest { @OptIn(ExperimentalPathApi::class) @Suppress("UnsafeCallOnNullableType") @@ -58,8 +35,8 @@ class DiktatRuleSetProviderTest { .filterNot { it in ignoredFileNames } .toList() val ruleNames = DiktatRuleSetProvider() + .invoke() .toKtLint() - .getRuleProviders() .map { it.createNewRuleInstance() } .asSequence() .onEachIndexed { index, rule -> @@ -88,5 +65,21 @@ class DiktatRuleSetProviderTest { private val ignoredRuleNames = listOf( "DummyWarning", ) + + /** + * Simple method to emulate [DiktatRuleSet] to inject `.yml` rule configuration and mock this part of code. + */ + internal fun diktatRuleSetForTest( + ruleSupplier: (rulesConfigList: List) -> DiktatRule, + rulesConfigList: List?, + ): DiktatRuleSet = run { + rulesConfigList ?: RulesConfigReader(Companion::class.java.classLoader) + .readResource("diktat-analysis.yml") + .orEmpty() + } + .let(ruleSupplier) + .let { + DiktatRuleSet(listOf(it)) + } } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index 935c026290..5a2f42205b 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -1,12 +1,16 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.ruleset.utils.FormatCallback import org.cqfn.diktat.ruleset.utils.defaultCallback -import org.cqfn.diktat.ruleset.utils.format +import org.cqfn.diktat.util.format import org.cqfn.diktat.test.framework.processing.FileComparisonResult import org.cqfn.diktat.test.framework.processing.TestComparatorUnit +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine +import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Rule import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Assertions @@ -30,12 +34,20 @@ open class FixTestBase( TestComparatorUnit( resourceFilePath = resourceFilePath, function = { expectedText, testFilePath -> - format( - ruleSetProviderRef = { DiktatRuleSetProvider4Test(ruleSupplier, overrideRulesConfigList ?: defaultRulesConfigList) }, - text = expectedText, - fileName = testFilePath, - cb = cb, + val ruleProviders = DiktatRuleSetProviderTest.diktatRuleSetForTest(ruleSupplier, overrideRulesConfigList ?: defaultRulesConfigList) + .toKtLint() + val ktLintRuleEngine = KtLintRuleEngine( + ruleProviders = ruleProviders ) + val code: Code = Code.CodeSnippet( + content = expectedText, + script = testFilePath.removeSuffix("_copy").endsWith("kts") + ) + ktLintRuleEngine.format(code) { error: LintError, corrected: Boolean -> + if (!corrected) { + cb(error, false) + } + } }, ) } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt index 0da0cff5b6..649f00ae49 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt @@ -1,6 +1,7 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.ruleset.rules.DiktatRule import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine @@ -77,8 +78,8 @@ open class LintTestBase(private val ruleSupplier: (rulesConfigList: List = mutableListOf() KtLintRuleEngine( - ruleProviders = DiktatRuleSetProvider4Test(ruleSupplier, - rulesConfigList ?: this.rulesConfigList).getRuleProviders(), + ruleProviders = DiktatRuleSetProviderTest.diktatRuleSetForTest(ruleSupplier, rulesConfigList ?: this.rulesConfigList) + .toKtLint(), ).lint( code = Code.CodeSnippet( content = code, diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index 56c03eea00..de1b49e92c 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -5,10 +5,14 @@ package org.cqfn.diktat.util import org.cqfn.diktat.ruleset.constants.EmitType +import org.cqfn.diktat.ruleset.utils.FormatCallback +import org.cqfn.diktat.ruleset.utils.defaultCallback import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.intellij.lang.annotations.Language @@ -132,3 +136,32 @@ internal fun applyToCode(@Language("kotlin") code: String, .`as`("Number of expected asserts") .isEqualTo(expectedAsserts) } + +/** + * @param ruleSetProviderRef + * @param text + * @param fileName + * @param cb callback to be called on unhandled [LintError]s + * @return formatted code + */ +@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER") +fun format( + ruleSetProviderRef: () -> RuleSetProviderV2, + @Language("kotlin") text: String, + fileName: String, + cb: FormatCallback = defaultCallback +): String { + val ruleProviders = ruleSetProviderRef().getRuleProviders() + val ktLintRuleEngine = KtLintRuleEngine( + ruleProviders = ruleProviders + ) + val code: Code = Code.CodeSnippet( + content = text, + script = fileName.removeSuffix("_copy").endsWith("kts") + ) + return ktLintRuleEngine.format(code) { error: LintError, corrected: Boolean -> + if (!corrected) { + cb(error, false) + } + } +} diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt index 2d12fcc289..3b217c2fee 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -2,8 +2,9 @@ package org.cqfn.diktat.ruleset.smoke import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import org.cqfn.diktat.ruleset.utils.format import org.cqfn.diktat.test.framework.processing.TestComparatorUnit +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.LintError import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach @@ -43,12 +44,19 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { private fun getTestComparatorUnit(config: Path) = TestComparatorUnit( resourceFilePath = RESOURCE_FILE_PATH, function = { expectedText, testFilePath -> - format( - ruleSetProviderRef = { DiktatRuleSetProvider(config.absolutePathString()).toKtLint() }, - text = expectedText, - fileName = testFilePath, - cb = { lintError, _ -> unfixedLintErrors.add(lintError) }, + val ruleProviders = DiktatRuleSetProvider(config.absolutePathString()).invoke().toKtLint() + val ktLintRuleEngine = KtLintRuleEngine( + ruleProviders = ruleProviders ) + val code: Code = Code.CodeSnippet( + content = expectedText, + script = testFilePath.removeSuffix("_copy").endsWith("kts") + ) + ktLintRuleEngine.format(code) { error: LintError, corrected: Boolean -> + if (!corrected) { + unfixedLintErrors.add(error) + } + } }, ) } From 9570c4450759de667cbd32959679aced6acc4fb4 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 5 Apr 2023 15:52:30 +0300 Subject: [PATCH 19/33] fixed merge issues --- .../ktlint/DiktatProcessorFactoryImpl.kt | 1 - .../ktlint/KtLintRuleSetProviderV2Wrapper.kt | 48 ------------- .../diktat/ktlint/KtLintRuleSetWrapper.kt | 37 ---------- .../cqfn/diktat/ktlint/KtLintRuleWrapper.kt | 14 ++++ .../org/cqfn/diktat/ktlint/KtLintUtils.kt | 70 +++++++------------ .../diktat/ktlint/KtLintRuleSetWrapperTest.kt | 6 -- .../rules/DiktatRuleSetProviderV2Spi.kt | 28 -------- .../ruleset/rules/DiktatRuleSetProviderSpi.kt | 25 ------- .../rules/DiktatRuleSetProviderV2Spi.kt | 37 ++++++++++ gradle/libs.versions.toml | 1 - 10 files changed, 76 insertions(+), 191 deletions(-) delete mode 100644 diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt delete mode 100644 diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt delete mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt create mode 100644 diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt index bfabd5aa6f..99c5d712dd 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt @@ -4,7 +4,6 @@ import org.cqfn.diktat.DiktatProcessor import org.cqfn.diktat.DiktatProcessorFactory import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint import org.cqfn.diktat.util.isKotlinScript import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.api.EditorConfigOverride diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt deleted file mode 100644 index 59c5b4e0e6..0000000000 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetProviderV2Wrapper.kt +++ /dev/null @@ -1,48 +0,0 @@ -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID -import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.asProvider -import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.ruleset.rules.DiktatRuleSet -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleProvider -import com.pinterest.ktlint.core.RuleSetProviderV2 - -/** - * This is a wrapper around __KtLint__'s [RuleSetProviderV2]. - */ -class KtLintRuleSetProviderV2Wrapper private constructor( - private val diktatRuleSetFactory: DiktatRuleSetProvider, -) : RuleSetProviderV2( - id = DIKTAT_RULE_SET_ID, - about = About( - maintainer = "Diktat", - description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", - license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", - repositoryUrl = "https://github.com/saveourtool/diktat", - issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", - ), -) { - override fun getRuleProviders(): Set = diktatRuleSetFactory().toKtLint() - - companion object { - private fun Sequence.wrapRules(): Sequence = runningFold(null as KtLintRuleWrapper?) { prevRule, diktatRule -> - KtLintRuleWrapper(diktatRule, prevRule) - }.filterNotNull() - - /** - * @return __KtLint__'s [RuleSetProviderV2] created from [DiktatRuleSetProvider] - */ - fun DiktatRuleSetProvider.toKtLint(): RuleSetProviderV2 = KtLintRuleSetProviderV2Wrapper(this) - - /** - * @return a set of __KtLint__'s [RuleProvider] created from [DiktatRuleSet] - */ - fun DiktatRuleSet.toKtLint(): Set = rules.asSequence() - .wrapRules() - .map { it.asProvider() } - .toSet() - } - -} diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt deleted file mode 100644 index 937a7b8457..0000000000 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapper.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.api.DiktatRule -import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID -import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSet - -/** - * This is a wrapper around __KtLint__'s [RuleSet] which adjusts visitorModifiers for all rules to keep order with prevRule - * Added as a workaround after introducing a new logic for sorting KtLint Rules: https://github.com/pinterest/ktlint/issues/1478 - * - * @param diktatRuleSet the rules which belong to the current [DiktatRuleSet]. - */ -class KtLintRuleSetWrapper private constructor( - diktatRuleSet: DiktatRuleSet, -) : RuleSet(DIKTAT_RULE_SET_ID, rules = wrapRules(diktatRuleSet.rules)) { - companion object { - /** - * @return __KtLint__'s [RuleSet] created from [DiktatRuleSet] - */ - fun DiktatRuleSet.toKtLint(): RuleSet = KtLintRuleSetWrapper(this) - - private fun wrapRules(rules: List): Array { - if (rules.isEmpty()) { - return emptyArray() - } - return rules.asSequence() - .runningFold(null as KtLintRuleWrapper?) { prevRule, diktatRule -> - KtLintRuleWrapper(diktatRule, prevRule) - } - .filterNotNull() - .toList() - .toTypedArray() - } - } -} diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt index 8666215c26..e7435e13d8 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapper.kt @@ -1,6 +1,7 @@ package org.cqfn.diktat.ktlint import org.cqfn.diktat.api.DiktatRule +import org.cqfn.diktat.api.DiktatRuleSet import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider @@ -26,6 +27,19 @@ class KtLintRuleWrapper( ) = rule.invoke(node, autoCorrect, emit) companion object { + private fun Sequence.wrapRules(): Sequence = runningFold(null as KtLintRuleWrapper?) { prevRule, diktatRule -> + KtLintRuleWrapper(diktatRule, prevRule) + }.filterNotNull() + + /** + * @return [Set] of __KtLint__'s [RuleProvider]s created from [DiktatRuleSet] + */ + fun DiktatRuleSet.toKtLint(): Set = rules + .asSequence() + .wrapRules() + .map { it.asProvider() } + .toSet() + private fun createVisitorModifiers( rule: DiktatRule, prevRule: KtLintRuleWrapper?, diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt index 82e5a70e0d..e1aa4dc22a 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt @@ -9,9 +9,9 @@ import org.cqfn.diktat.api.DiktatRuleSet import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.unwrap import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint -import com.pinterest.ktlint.core.KtLint -import com.pinterest.ktlint.core.KtLint.ExperimentalParams +import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.LintError import mu.KotlinLogging import org.intellij.lang.annotations.Language @@ -61,28 +61,6 @@ fun DiktatCallback.unwrap(): LintErrorCallback = { error, isCorrected -> this(error.wrap(), isCorrected) } -/** - * Enables ignoring autocorrected errors when in "fix" mode (i.e. when - * [KtLint.format] is invoked). - * - * Before version 0.47, _Ktlint_ only reported non-corrected errors in "fix" - * mode. - * Now, this has changed. - * - * @receiver the instance of _Ktlint_ parameters. - * @return the instance with the [callback][ExperimentalParams.cb] modified in - * such a way that it ignores corrected errors. - * @see KtLint.format - * @see ExperimentalParams.cb - * @since 1.2.4 - */ -private fun ExperimentalParams.ignoreCorrectedErrors(): ExperimentalParams = - copy(cb = { error: LintError, corrected: Boolean -> - if (!corrected) { - cb(error, false) - } - }) - /** * @param ruleSetSupplier * @param text @@ -97,17 +75,19 @@ fun format( fileName: String, cb: DiktatCallback = defaultCallback ): String { - val ruleSets = listOf(ruleSetSupplier().toKtLint()) - return KtLint.format( - ExperimentalParams( - text = text, - ruleSets = ruleSets, - fileName = fileName.removeSuffix("_copy"), - script = fileName.removeSuffix("_copy").endsWith("kts"), - cb = cb.unwrap(), - debug = true, - ).ignoreCorrectedErrors() + val ktLintRuleEngine = KtLintRuleEngine( + ruleProviders = ruleSetSupplier().toKtLint() ) + return ktLintRuleEngine.format( + code = Code.CodeSnippet( + content = text, + script = fileName.removeSuffix("_copy").endsWith("kts"), + ) + ) { lintError: LintError, isCorrected: Boolean -> + if (!isCorrected) { + cb(lintError.wrap(), false) + } + } } /** @@ -124,15 +104,15 @@ fun lint( fileName: String = "test.ks", cb: DiktatCallback = DiktatCallback.empty ) { - val ruleSets = listOf(ruleSetSupplier().toKtLint()) - KtLint.lint( - ExperimentalParams( - text = text, - ruleSets = ruleSets, - fileName = fileName.removeSuffix("_copy"), - script = fileName.removeSuffix("_copy").endsWith("kts"), - cb = cb.unwrap(), - debug = true, - ).ignoreCorrectedErrors() + val ktLintRuleEngine = KtLintRuleEngine( + ruleProviders = ruleSetSupplier().toKtLint() ) + return ktLintRuleEngine.lint( + code = Code.CodeSnippet( + content = text, + script = fileName.removeSuffix("_copy").endsWith("kts"), + ) + ) { lintError: LintError -> + cb(lintError.wrap(), false) + } } diff --git a/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt b/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt index dcb13ec0bd..893cbbb87e 100644 --- a/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt +++ b/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt @@ -1,18 +1,12 @@ package org.cqfn.diktat.ktlint <<<<<<<< HEAD:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt -import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint -import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.delegatee -import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.ruleset.rules.DiktatRuleSet import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine ======== import org.cqfn.diktat.api.DiktatErrorEmitter import org.cqfn.diktat.api.DiktatRule import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.unwrap import com.pinterest.ktlint.core.KtLint >>>>>>>> origin/master:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt deleted file mode 100644 index a1e13ba209..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.cqfn.diktat.ruleset.rules - -import org.cqfn.diktat.ktlint.KtLintRuleSetProviderV2Wrapper.Companion.toKtLint -import com.pinterest.ktlint.core.RuleProvider -import com.pinterest.ktlint.core.RuleSetProviderV2 - -/** - * [RuleSetProviderV2] that provides diKTat ruleset. - * - * By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run - * otherwise it will use default configuration where some rules are disabled. - * - * This class is registered in [resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2] - */ -class DiktatRuleSetProviderV2Spi private constructor( - private val ruleSetProviderV2: RuleSetProviderV2 -) : RuleSetProviderV2( - id = ruleSetProviderV2.id, - about = ruleSetProviderV2.about, -) { - /** - * The no-argument constructor is used by the Java SPI interface. - */ - @Suppress("unused") - constructor() : this(DiktatRuleSetProvider().toKtLint()) - - override fun getRuleProviders(): Set = ruleSetProviderV2.getRuleProviders() -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt deleted file mode 100644 index 0a3b744bc7..0000000000 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderSpi.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.cqfn.diktat.ruleset.rules - -import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint -import com.pinterest.ktlint.core.RuleSet -import com.pinterest.ktlint.core.RuleSetProvider -import com.pinterest.ktlint.core.initKtLintKLogger -import mu.KotlinLogging -import org.slf4j.Logger - -/** - * [RuleSetProvider] that provides diKTat ruleset. - * - * By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run - * otherwise it will use default configuration where some rules are disabled. - * - * This class is registered in [resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider] - */ -class DiktatRuleSetProviderSpi : RuleSetProvider { - init { - // Need to init KtLint logger to set log level from CLI - KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).initKtLintKLogger() - } - - override fun get(): RuleSet = DiktatRuleSetProvider().invoke().toKtLint() -} diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt new file mode 100644 index 0000000000..7d4d5b8129 --- /dev/null +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV2Spi.kt @@ -0,0 +1,37 @@ +package org.cqfn.diktat.ruleset.rules + +import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID +import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint +import com.pinterest.ktlint.core.RuleProvider +import com.pinterest.ktlint.core.RuleSetProviderV2 +import com.pinterest.ktlint.core.initKtLintKLogger +import mu.KotlinLogging +import org.slf4j.Logger + +/** + * [RuleSetProviderV2] that provides diKTat ruleset. + * + * By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run + * otherwise it will use default configuration where some rules are disabled. + * + * This class is registered in [resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProviderV2] + * + * The no-argument constructor is used by the Java SPI interface. + */ +class DiktatRuleSetProviderV2Spi : RuleSetProviderV2( + id = DIKTAT_RULE_SET_ID, + about = About( + maintainer = "Diktat", + description = "Strict coding standard for Kotlin and a custom set of rules for detecting code smells, code style issues, and bugs", + license = "https://github.com/saveourtool/diktat/blob/master/LICENSE", + repositoryUrl = "https://github.com/saveourtool/diktat", + issueTrackerUrl = "https://github.com/saveourtool/diktat/issues", + ), +) { + init { + // Need to init KtLint logger to set log level from CLI + KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).initKtLintKLogger() + } + + override fun getRuleProviders(): Set = DiktatRuleSetProvider().invoke().toKtLint() +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ca382be6ed..c1ee9fe26b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,6 @@ jupiter-itf-extension = "0.12.0" mockito-all = "1.10.19" # main class in executable jar -log4j2 = "2.20.0" kotlinx-cli = "0.3.5" From bad26d08bd87d4af4beddd7158113da9e6b61240 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 5 Apr 2023 16:41:57 +0300 Subject: [PATCH 20/33] fixed merge issues --- .../diktat/ktlint/KtLintRuleSetWrapperTest.kt | 41 ++++--------------- .../diktat/util/DiktatRuleSetProviderTest.kt | 15 ------- .../org/cqfn/diktat/util/FixTestBase.kt | 3 +- .../org/cqfn/diktat/util/LintTestBase.kt | 5 ++- 4 files changed, 14 insertions(+), 50 deletions(-) diff --git a/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt b/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt index 893cbbb87e..343e64d25a 100644 --- a/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt +++ b/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt @@ -1,15 +1,12 @@ package org.cqfn.diktat.ktlint -<<<<<<<< HEAD:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine -======== import org.cqfn.diktat.api.DiktatErrorEmitter import org.cqfn.diktat.api.DiktatRule import org.cqfn.diktat.api.DiktatRuleSet +import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.unwrap -import com.pinterest.ktlint.core.KtLint ->>>>>>>> origin/master:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleProvider import org.assertj.core.api.Assertions.assertThat @@ -79,31 +76,18 @@ class KtLintRuleWrapperTest { /* * Make sure OrderedRuleSet preserves the order. */ -<<<<<<<< HEAD:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt val ruleProviders = DiktatRuleSet(rules).toKtLint() - assertThat(ruleProviders.map(RuleProvider::createNewRuleInstance).map(Rule::id)).containsExactlyElementsOf(rules.map(DiktatRule::id)) -======== - val ruleSet = DiktatRuleSet(rules).toKtLint() - assertThat(ruleSet.rules.map(Rule::id)).containsExactlyElementsOf(rules.map(DiktatRule::id).map { it.qualifiedWithRuleSetId() }) ->>>>>>>> origin/master:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt + assertThat(ruleProviders.map(RuleProvider::createNewRuleInstance).map(Rule::id)) + .containsExactlyElementsOf(rules.map(DiktatRule::id).map { it.qualifiedWithRuleSetId() }) @Language("kotlin") val code = "fun foo() { }" -<<<<<<<< HEAD:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt KtLintRuleEngine( ruleProviders = ruleProviders ).lint( code = Code.CodeSnippet( content = code -======== - KtLint.lint( - KtLint.ExperimentalParams( - fileName = "TestFileName.kt", - text = code, - ruleSets = listOf(ruleSet), - cb = { _, _ -> }, ->>>>>>>> origin/master:diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt ) ) @@ -141,20 +125,13 @@ class KtLintRuleWrapperTest { * | * V * C(File) -> C(Node) -> C(Leaf) - * - * val expectedRuleInvocationOrder = rules.asSequence() - * .map(Rule::id) - * .flatMap { ruleId -> - * generateSequence { ruleId }.take(astNodeCount) - * } - * .toList() */ - val expectedRuleInvocationOrder = generateSequence { - rules.map(DiktatRule::id) - } - .take(astNodeCount) - .flatten() - .toList() + val expectedRuleInvocationOrder = rules.asSequence() + .map(DiktatRule::id) + .flatMap { ruleId -> + generateSequence { ruleId }.take(astNodeCount) + } + .toList() assertThat(actualRuleInvocationOrder) .containsExactlyElementsOf(expectedRuleInvocationOrder) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt index accae0655d..2ea069c128 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt @@ -5,7 +5,6 @@ package org.cqfn.diktat.util import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.api.DiktatRuleSetFactory import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.RulesConfigReader import org.cqfn.diktat.ruleset.rules.DiktatRule @@ -22,20 +21,6 @@ import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension import kotlin.io.path.walk -/** - * simple class for emulating RuleSetProvider to inject .yml rule configuration and mock this part of code - */ -class DiktatRuleSetProvider4Test( - private val ruleSupplier: (rulesConfigList: List) -> DiktatRule, - rulesConfigList: List?, -) : DiktatRuleSetFactory { - private val rulesConfigList: List? = rulesConfigList ?: RulesConfigReader(javaClass.classLoader).readResource("diktat-analysis.yml") - - override fun invoke(): DiktatRuleSet = DiktatRuleSet(listOf(ruleSupplier.invoke(rulesConfigList ?: emptyList()))) - - override fun create(configFile: String): DiktatRuleSet = throw IllegalStateException("Method is not supported for testing") -} - class DiktatRuleSetProviderTest { @OptIn(ExperimentalPathApi::class) @Suppress("UnsafeCallOnNullableType") diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index c90af420a8..e47feac007 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -7,6 +7,7 @@ import org.cqfn.diktat.ktlint.format import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.test.framework.processing.FileComparisonResult import org.cqfn.diktat.test.framework.processing.TestComparatorUnit +import org.cqfn.diktat.util.DiktatRuleSetProviderTest.Companion.diktatRuleSetForTest import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Assertions import java.nio.file.Path @@ -30,7 +31,7 @@ open class FixTestBase( resourceFilePath = resourceFilePath, function = { expectedText, testFilePath -> format( - ruleSetSupplier = { DiktatRuleSetProvider4Test(ruleSupplier, overrideRulesConfigList ?: defaultRulesConfigList).invoke() }, + ruleSetSupplier = { diktatRuleSetForTest(ruleSupplier, overrideRulesConfigList ?: defaultRulesConfigList) }, text = expectedText, fileName = testFilePath, cb = cb, diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt index b4df30bff6..d8e81e411c 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/LintTestBase.kt @@ -4,6 +4,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.unwrap import org.cqfn.diktat.ktlint.lint import org.cqfn.diktat.ruleset.rules.DiktatRule +import org.cqfn.diktat.util.DiktatRuleSetProviderTest.Companion.diktatRuleSetForTest import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Rule import org.assertj.core.api.Assertions.assertThat @@ -77,8 +78,8 @@ open class LintTestBase(private val ruleSupplier: (rulesConfigList: List = mutableListOf() lint( - ruleSetSupplier = { DiktatRuleSetProvider4Test(ruleSupplier, - rulesConfigList ?: this.rulesConfigList).invoke() }, + ruleSetSupplier = { diktatRuleSetForTest(ruleSupplier, + rulesConfigList ?: this.rulesConfigList) }, text = code, fileName = actualFileName, cb = { lintError, _ -> lintErrors += lintError.unwrap() }, From a32b06b180d2081d44178b461bf8101b7b27b227 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 5 Apr 2023 17:52:46 +0300 Subject: [PATCH 21/33] reused DiktatProcessor for ad-hook format and lint --- .../kotlin/org/cqfn/diktat/DiktatProcessor.kt | 20 ++++++ .../ktlint/DiktatProcessorFactoryImpl.kt | 55 +++++++++------ .../org/cqfn/diktat/ktlint/KtLintUtils.kt | 67 ++++--------------- .../org/cqfn/diktat/util/FixTestBase.kt | 10 ++- 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt index 13b5be4c5f..68e3ba58a1 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt @@ -16,6 +16,16 @@ interface DiktatProcessor { */ fun fix(file: Path, callback: DiktatCallback): String + /** + * Run `diktat fix` on provided [code] using [callback] for detected errors and returned formatted code. + * + * @param code + * @param isScript + * @param callback + * @return result of `diktat fix` + */ + fun fix(code: String, isScript: Boolean, callback: DiktatCallback): String + /** * Run `diktat check` on provided [file] using [callback] for detected errors. * @@ -23,4 +33,14 @@ interface DiktatProcessor { * @param callback */ fun check(file: Path, callback: DiktatCallback) + + + /** + * Run `diktat check` on provided [code] using [callback] for detected errors. + * + * @param code + * @param isScript + * @param callback + */ + fun check(code: String, isScript: Boolean, callback: DiktatCallback) } diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt index 99c5d712dd..155b6d23cb 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt @@ -4,37 +4,48 @@ import org.cqfn.diktat.DiktatProcessor import org.cqfn.diktat.DiktatProcessorFactory import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.util.isKotlinScript -import com.pinterest.ktlint.core.KtLint -import com.pinterest.ktlint.core.api.EditorConfigOverride -import java.nio.charset.StandardCharsets +import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap +import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint +import com.pinterest.ktlint.core.Code +import com.pinterest.ktlint.core.KtLintRuleEngine +import com.pinterest.ktlint.core.LintError import java.nio.file.Path -import kotlin.io.path.absolutePathString -import kotlin.io.path.readText + +private typealias FormatCallback = (LintError, Boolean) -> Unit +private typealias LintCallback = (LintError) -> Unit /** * A factory to create [DiktatProcessor] using [DiktatProcessorFactory] and `KtLint` as engine */ class DiktatProcessorFactoryImpl : DiktatProcessorFactory { override fun invoke(diktatRuleSet: DiktatRuleSet): DiktatProcessor = object : DiktatProcessor { - override fun fix(file: Path, callback: DiktatCallback): String = KtLint.format(ktLintParams(diktatRuleSet, file, callback.unwrap())) - override fun check(file: Path, callback: DiktatCallback) = KtLint.lint(ktLintParams(diktatRuleSet, file, callback.unwrap())) + override fun fix(file: Path, callback: DiktatCallback): String = + ktlintEngine(diktatRuleSet).format(file.toKtLint(), callback.toKtLintForFormat()) + override fun fix(code: String, isScript: Boolean, callback: DiktatCallback): String = + ktlintEngine(diktatRuleSet).format(code.toKtLint(isScript), callback.toKtLintForFormat()) + override fun check(file: Path, callback: DiktatCallback) = + ktlintEngine(diktatRuleSet).lint(file.toKtLint(), callback.toKtLintForLint()) + override fun check(code: String, isScript: Boolean, callback: DiktatCallback) = + ktlintEngine(diktatRuleSet).lint(code.toKtLint(isScript), callback.toKtLintForLint()) } - private fun ktLintParams( + private fun ktlintEngine( diktatRuleSet: DiktatRuleSet, - file: Path, - callback: LintErrorCallback, - ): KtLint.ExperimentalParams = KtLint.ExperimentalParams( - fileName = file.absolutePathString(), - text = file.readText(StandardCharsets.UTF_8), - ruleSets = setOf(diktatRuleSet.toKtLint()), - userData = emptyMap(), - cb = callback, - script = file.isKotlinScript(), - editorConfigPath = null, - debug = false, // we do not use it - editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride, - isInvokedFromCli = false + ): KtLintRuleEngine = KtLintRuleEngine( + ruleProviders = diktatRuleSet.toKtLint() ) + + companion object { + private fun Path.toKtLint(): Code = Code.CodeFile(this.toFile()) + + private fun String.toKtLint(isScript: Boolean): Code = Code.CodeSnippet(this, isScript) + + private fun DiktatCallback.toKtLintForFormat(): FormatCallback = { error, isCorrected -> + this(error.wrap(), isCorrected) + } + + private fun DiktatCallback.toKtLintForLint(): LintCallback = { error -> + this(error.wrap(), false) + } + } } diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt index e1aa4dc22a..a300e4df51 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt @@ -7,11 +7,6 @@ package org.cqfn.diktat.ktlint import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.api.DiktatRuleSet import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID -import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.unwrap -import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap -import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint -import com.pinterest.ktlint.core.Code -import com.pinterest.ktlint.core.KtLintRuleEngine import com.pinterest.ktlint.core.LintError import mu.KotlinLogging import org.intellij.lang.annotations.Language @@ -19,14 +14,6 @@ import java.nio.file.Path import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.relativeTo -private val log = KotlinLogging.logger { } - -val defaultCallback = DiktatCallback { error, _ -> - log.warn { "Received linting error: $error" } -} - -typealias LintErrorCallback = (LintError, Boolean) -> Unit - /** * Makes sure this _rule id_ is qualified with a _rule set id_. * @@ -47,20 +34,6 @@ fun String.qualifiedWithRuleSetId(ruleSetId: String = DIKTAT_RULE_SET_ID): Strin */ fun Path.relativePathStringTo(sourceRootDir: Path): String = relativeTo(sourceRootDir).invariantSeparatorsPathString -/** - * @return [DiktatCallback] from KtLint [LintErrorCallback] - */ -fun LintErrorCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected -> - this(error.unwrap(), isCorrected) -} - -/** - * @return KtLint [LintErrorCallback] from [DiktatCallback] or exception - */ -fun DiktatCallback.unwrap(): LintErrorCallback = { error, isCorrected -> - this(error.wrap(), isCorrected) -} - /** * @param ruleSetSupplier * @param text @@ -73,22 +46,13 @@ fun format( ruleSetSupplier: () -> DiktatRuleSet, @Language("kotlin") text: String, fileName: String, - cb: DiktatCallback = defaultCallback -): String { - val ktLintRuleEngine = KtLintRuleEngine( - ruleProviders = ruleSetSupplier().toKtLint() + cb: DiktatCallback, +): String = DiktatProcessorFactoryImpl().invoke(ruleSetSupplier()) + .fix( + code = text, + isScript = fileName.removeSuffix("_copy").endsWith("kts"), + callback = cb, ) - return ktLintRuleEngine.format( - code = Code.CodeSnippet( - content = text, - script = fileName.removeSuffix("_copy").endsWith("kts"), - ) - ) { lintError: LintError, isCorrected: Boolean -> - if (!isCorrected) { - cb(lintError.wrap(), false) - } - } -} /** * @param ruleSetSupplier @@ -101,18 +65,11 @@ fun format( fun lint( ruleSetSupplier: () -> DiktatRuleSet, @Language("kotlin") text: String, - fileName: String = "test.ks", + fileName: String = "test.kt", cb: DiktatCallback = DiktatCallback.empty -) { - val ktLintRuleEngine = KtLintRuleEngine( - ruleProviders = ruleSetSupplier().toKtLint() +) = DiktatProcessorFactoryImpl().invoke(ruleSetSupplier()) + .check( + code = text, + isScript = fileName.removeSuffix("_copy").endsWith("kts"), + callback = cb, ) - return ktLintRuleEngine.lint( - code = Code.CodeSnippet( - content = text, - script = fileName.removeSuffix("_copy").endsWith("kts"), - ) - ) { lintError: LintError -> - cb(lintError.wrap(), false) - } -} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index e47feac007..69eb06d372 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -2,12 +2,12 @@ package org.cqfn.diktat.util import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.common.config.rules.RulesConfig -import org.cqfn.diktat.ktlint.defaultCallback import org.cqfn.diktat.ktlint.format import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.test.framework.processing.FileComparisonResult import org.cqfn.diktat.test.framework.processing.TestComparatorUnit import org.cqfn.diktat.util.DiktatRuleSetProviderTest.Companion.diktatRuleSetForTest +import mu.KotlinLogging import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Assertions import java.nio.file.Path @@ -100,4 +100,12 @@ open class FixTestBase( return testComparatorUnit .compareFilesFromFileSystem(expected, actual, false) } + + companion object { + private val log = KotlinLogging.logger { } + + private val defaultCallback = DiktatCallback { error, _ -> + log.warn { "Received linting error: $error" } + } + } } From 9e02d9ebddb8dbc6ce3b7b9d42c756326cbd8269 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 6 Apr 2023 14:51:19 +0300 Subject: [PATCH 22/33] cleanup old code --- .../kotlin/org/cqfn/diktat/DiktatProcessor.kt | 13 +- .../ktlint/DiktatProcessorFactoryImpl.kt | 20 ++- .../org/cqfn/diktat/ktlint/KtLintUtils.kt | 1 - .../diktat-runner-cli/build.gradle.kts | 21 --- .../main/kotlin/org/cqfn/diktat/DiktatMain.kt | 86 --------- .../org/cqfn/diktat/cli/DiktatProperties.kt | 165 ------------------ .../kotlin/org/cqfn/diktat/util/CliUtils.kt | 42 ----- .../kotlin/org/cqfn/diktat/util/FileUtils.kt | 36 ---- .../org/cqfn/diktat/ktlint/ReporterUtil.kt | 91 ---------- 9 files changed, 24 insertions(+), 451 deletions(-) delete mode 100644 diktat-runner/diktat-runner-cli/build.gradle.kts delete mode 100644 diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt delete mode 100644 diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt delete mode 100644 diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt delete mode 100644 diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt delete mode 100644 diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt index 68e3ba58a1..ef8b1ebfe2 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt @@ -24,7 +24,11 @@ interface DiktatProcessor { * @param callback * @return result of `diktat fix` */ - fun fix(code: String, isScript: Boolean, callback: DiktatCallback): String + fun fix( + code: String, + isScript: Boolean, + callback: DiktatCallback + ): String /** * Run `diktat check` on provided [file] using [callback] for detected errors. @@ -34,7 +38,6 @@ interface DiktatProcessor { */ fun check(file: Path, callback: DiktatCallback) - /** * Run `diktat check` on provided [code] using [callback] for detected errors. * @@ -42,5 +45,9 @@ interface DiktatProcessor { * @param isScript * @param callback */ - fun check(code: String, isScript: Boolean, callback: DiktatCallback) + fun check( + code: String, + isScript: Boolean, + callback: DiktatCallback + ) } diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt index 155b6d23cb..43fe27d2c0 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt @@ -20,13 +20,21 @@ private typealias LintCallback = (LintError) -> Unit class DiktatProcessorFactoryImpl : DiktatProcessorFactory { override fun invoke(diktatRuleSet: DiktatRuleSet): DiktatProcessor = object : DiktatProcessor { override fun fix(file: Path, callback: DiktatCallback): String = - ktlintEngine(diktatRuleSet).format(file.toKtLint(), callback.toKtLintForFormat()) - override fun fix(code: String, isScript: Boolean, callback: DiktatCallback): String = - ktlintEngine(diktatRuleSet).format(code.toKtLint(isScript), callback.toKtLintForFormat()) + ktlintEngine(diktatRuleSet).format(file.toKtLint(), callback.toKtLintForFormat()) + override fun fix( + code: String, + isScript: Boolean, + callback: DiktatCallback + ): String = + ktlintEngine(diktatRuleSet).format(code.toKtLint(isScript), callback.toKtLintForFormat()) override fun check(file: Path, callback: DiktatCallback) = - ktlintEngine(diktatRuleSet).lint(file.toKtLint(), callback.toKtLintForLint()) - override fun check(code: String, isScript: Boolean, callback: DiktatCallback) = - ktlintEngine(diktatRuleSet).lint(code.toKtLint(isScript), callback.toKtLintForLint()) + ktlintEngine(diktatRuleSet).lint(file.toKtLint(), callback.toKtLintForLint()) + override fun check( + code: String, + isScript: Boolean, + callback: DiktatCallback + ) = + ktlintEngine(diktatRuleSet).lint(code.toKtLint(isScript), callback.toKtLintForLint()) } private fun ktlintEngine( diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt index a300e4df51..67015476ff 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/KtLintUtils.kt @@ -8,7 +8,6 @@ import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.api.DiktatRuleSet import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import com.pinterest.ktlint.core.LintError -import mu.KotlinLogging import org.intellij.lang.annotations.Language import java.nio.file.Path import kotlin.io.path.invariantSeparatorsPathString diff --git a/diktat-runner/diktat-runner-cli/build.gradle.kts b/diktat-runner/diktat-runner-cli/build.gradle.kts deleted file mode 100644 index 12d376f8dc..0000000000 --- a/diktat-runner/diktat-runner-cli/build.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id("org.cqfn.diktat.buildutils.kotlin-jvm-configuration") - id("org.cqfn.diktat.buildutils.code-quality-convention") - id("org.cqfn.diktat.buildutils.publishing-signing-default-configuration") -} - -project.description = "This module builds diktat-runner implementation using ktlint as CLI" - -dependencies { - api(projects.diktatRunner.diktatRunnerApi) - implementation(projects.diktatRules) - implementation(projects.diktatRunner.diktatRunnerKtlintEngine) - implementation(libs.kotlinx.cli) - implementation(libs.log4j2.core) - implementation(libs.ktlint.reporter.baseline) - implementation(libs.ktlint.reporter.checkstyle) - implementation(libs.ktlint.reporter.html) - implementation(libs.ktlint.reporter.json) - implementation(libs.ktlint.reporter.plain) - implementation(libs.ktlint.reporter.sarif) -} diff --git a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt deleted file mode 100644 index 5c87f8dd0d..0000000000 --- a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The file contains main method - */ - -package org.cqfn.diktat - -import org.cqfn.diktat.api.DiktatError -import org.cqfn.diktat.api.DiktatMode -import org.cqfn.diktat.cli.DiktatProperties -import org.cqfn.diktat.common.utils.loggerWithKtlintConfig -import org.cqfn.diktat.ktlint.unwrap -import org.cqfn.diktat.ruleset.utils.isKotlinCodeOrScript -import org.cqfn.diktat.util.tryToPathIfExists -import org.cqfn.diktat.util.walkByGlob -import mu.KotlinLogging -import java.nio.file.Paths -import kotlin.io.path.absolutePathString -import kotlin.io.path.writeText - -@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") -private val log = KotlinLogging.loggerWithKtlintConfig {} - -typealias DiktatErrorWithCorrectionInfo = Pair - -@Suppress( - "LongMethod", - "TOO_LONG_FUNCTION" -) -fun main(args: Array) { - val properties = DiktatProperties.parse(args) - properties.configureLogger() - - log.debug { - "Loading diktatRuleSet using config ${properties.config}" - } - val diktatProcessor = DiktatProcessor.builder() - .diktatRuleSetProvider(properties.config) - .build() - val reporter = properties.reporter() - reporter.beforeAll() - - log.debug { - "Resolving files by patterns: ${properties.patterns}" - } - val currentFolder = Paths.get(".") - properties.patterns - .asSequence() - .flatMap { pattern -> - pattern.tryToPathIfExists()?.let { sequenceOf(it) } - ?: currentFolder.walkByGlob(pattern) - } - .filter { file -> file.isKotlinCodeOrScript() } - .distinct() - .map { it.normalize() } - .map { file -> - log.debug { - "Start processing the file: $file" - } - val result: MutableList = mutableListOf() - DiktatProcessCommand.builder() - .processor(diktatProcessor) - .file(file) - .callback { error, isCorrected -> - result.add(error to isCorrected) - } - .build() - .let { command -> - when (properties.mode) { - DiktatMode.CHECK -> command.check() - DiktatMode.FIX -> { - val formattedFileContent = command.fix() - file.writeText(formattedFileContent, Charsets.UTF_8) - } - } - } - file to result - } - .forEach { (file, result) -> - reporter.before(file.absolutePathString()) - result.forEach { (error, isCorrected) -> - reporter.onLintError(file.absolutePathString(), error.unwrap(), isCorrected) - } - reporter.after(file.absolutePathString()) - } - reporter.afterAll() -} diff --git a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt deleted file mode 100644 index 8f289f702e..0000000000 --- a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt +++ /dev/null @@ -1,165 +0,0 @@ -package org.cqfn.diktat.cli - -import org.cqfn.diktat.api.DiktatMode -import org.cqfn.diktat.common.config.rules.DIKTAT -import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF -import org.cqfn.diktat.ktlint.buildReporter -import org.cqfn.diktat.util.colorName -import org.cqfn.diktat.util.reporterProviderId -import com.pinterest.ktlint.core.Reporter -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.core.LoggerContext -import org.slf4j.event.Level -import kotlin.system.exitProcess -import kotlinx.cli.ArgParser -import kotlinx.cli.ArgType -import kotlinx.cli.default -import kotlinx.cli.vararg - -/** - * @property config path to `diktat-analysis.yml` - * @property mode mode of `diktat` - * @property reporterProviderId - * @property output - * @property patterns - */ -data class DiktatProperties( - val config: String, - val mode: DiktatMode, - val reporterProviderId: String, - val output: String?, - private val groupByFileInPlain: Boolean, - private val colorNameInPlain: String?, - private val logLevel: Level, - val patterns: List, -) { - /** - * @return a configured [Reporter] - */ - fun reporter(): Reporter = buildReporter( - reporterProviderId, output, colorNameInPlain, groupByFileInPlain, mode - ) - - /** - * Configure logger level using [logLevel] - */ - fun configureLogger() { - // set log level - LogManager.getContext(false) - .let { it as LoggerContext } - .also { ctx -> - ctx.configuration.rootLogger.level = when (logLevel) { - Level.ERROR -> org.apache.logging.log4j.Level.ERROR - Level.WARN -> org.apache.logging.log4j.Level.WARN - Level.INFO -> org.apache.logging.log4j.Level.INFO - Level.DEBUG -> org.apache.logging.log4j.Level.DEBUG - Level.TRACE -> org.apache.logging.log4j.Level.TRACE - } - } - .updateLoggers() - } - - companion object { - /** - * @param args cli arguments - * @return parsed [DiktatProperties] - */ - @Suppress( - "LongMethod", - "TOO_LONG_FUNCTION" - ) - fun parse(args: Array): DiktatProperties { - val parser = ArgParser(DIKTAT) - val config: String by parser.option( - type = ArgType.String, - fullName = "config", - shortName = "c", - description = "Specify the location of the YAML configuration file. By default, $DIKTAT_ANALYSIS_CONF in the current directory is used.", - ).default(DIKTAT_ANALYSIS_CONF) - val mode: DiktatMode by parser.option( - type = ArgType.Choice(), - fullName = "mode", - shortName = "m", - description = "Mode of `diktat` controls that `diktat` fixes or only finds any deviations from the code style." - ).default(DiktatMode.CHECK) - val reporterProviderId: String by parser.reporterProviderId() - val output: String? by parser.option( - type = ArgType.String, - fullName = "output", - shortName = "o", - description = "Redirect the reporter output to a file.", - ) - val groupByFileInPlain: Boolean by parser.option( - type = ArgType.Boolean, - fullName = "plain-group-by-file", - shortName = null, - description = "A flag for plain reporter" - ).default(false) - val colorName: String? by parser.colorName() - val logLevel: Level by parser.option( - type = ArgType.Choice(), - fullName = "log-level", - shortName = "l", - description = "Enable the output with specific level", - ).default(Level.INFO) - val patterns: List by parser.argument( - type = ArgType.String, - description = "A list of files to process by diktat" - ).vararg() - - parser.addOptionAndShowResourceWithExit( - fullName = "version", - shortName = "V", - description = "Output version information and exit.", - args = args, - resourceName = "META-INF/diktat/version" - ) - parser.addOptionAndShowResourceWithExit( - fullName = "license", - shortName = null, - description = "Display the license and exit.", - args = args, - resourceName = "META-INF/diktat/LICENSE", - ) - - parser.parse(args) - return DiktatProperties( - config = config, - mode = mode, - reporterProviderId = reporterProviderId, - output = output, - groupByFileInPlain = groupByFileInPlain, - colorNameInPlain = colorName, - logLevel = logLevel, - patterns = patterns, - ) - } - - private fun ArgParser.addOptionAndShowResourceWithExit( - fullName: String, - shortName: String?, - description: String, - args: Array, - resourceName: String, - ) { - // add here to print in help - option( - type = ArgType.Boolean, - fullName = fullName, - shortName = shortName, - description = description - ) - if (args.contains("--$fullName") || shortName?.let { args.contains("-$it") } == true) { - @Suppress("DEBUG_PRINT", "ForbiddenMethodCall") - print(readFromResource(resourceName)) - exitProcess(0) - } - } - - private fun readFromResource(resourceName: String): String = DiktatProperties::class.java - .classLoader - .getResource(resourceName) - ?.readText() - ?: error("Resource $resourceName not found") - } -} diff --git a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt deleted file mode 100644 index d1da8ec262..0000000000 --- a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/CliUtils.kt +++ /dev/null @@ -1,42 +0,0 @@ -/** - * This class contains util methods to operate with kotlinx.cli.* - */ - -package org.cqfn.diktat.util - -import org.cqfn.diktat.ktlint.colorNamesForPlainReporter -import org.cqfn.diktat.ktlint.plainReporterProvider -import org.cqfn.diktat.ktlint.reporterProviders -import com.pinterest.ktlint.core.ReporterProvider -import kotlinx.cli.ArgParser -import kotlinx.cli.ArgType -import kotlinx.cli.default - -/** - * @return a single [ReporterProvider] as parsed cli arg - */ -internal fun ArgParser.reporterProviderId() = option( - type = ArgType.Choice( - choices = reporterProviders.keys.toList(), - toVariant = { it }, - variantToString = { it }, - ), - fullName = "reporter", - shortName = "r", - description = "The reporter to use" -) - .default(plainReporterProvider.id) - -/** - * @return a single and optional color name as parsed cli args - */ -internal fun ArgParser.colorName() = this.option( - type = ArgType.Choice( - choices = colorNamesForPlainReporter, - toVariant = { it }, - variantToString = { it }, - ), - fullName = "plain-color", - shortName = null, - description = "Colorize the output.", -) diff --git a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt b/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt deleted file mode 100644 index 22f5027272..0000000000 --- a/diktat-runner/diktat-runner-cli/src/main/kotlin/org/cqfn/diktat/util/FileUtils.kt +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This class contains util methods to operate with java.nio.file.Path - */ - -package org.cqfn.diktat.util - -import java.nio.file.InvalidPathException -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.PathWalkOption -import kotlin.io.path.exists -import kotlin.io.path.walk - -/** - * Create a matcher and return a filter that uses it. - * - * @param glob glob pattern to filter files - * @return a sequence of files which matches to [glob] - */ -@OptIn(ExperimentalPathApi::class) -fun Path.walkByGlob(glob: String): Sequence = - fileSystem.getPathMatcher("glob:$glob") - .let { matcher -> - this.walk(PathWalkOption.INCLUDE_DIRECTORIES) - .filter { matcher.matches(it) } - } - -/** - * @return path or null if path is invalid or doesn't exist - */ -fun String.tryToPathIfExists(): Path? = try { - Paths.get(this).takeIf { it.exists() } -} catch (e: InvalidPathException) { - null -} diff --git a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt b/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt deleted file mode 100644 index a9717ad304..0000000000 --- a/diktat-runner/diktat-runner-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/ReporterUtil.kt +++ /dev/null @@ -1,91 +0,0 @@ -/** - * File contains util methods to create ktlint's [Reporter] - */ - -package org.cqfn.diktat.ktlint - -import org.cqfn.diktat.api.DiktatMode -import com.pinterest.ktlint.core.Reporter -import com.pinterest.ktlint.core.ReporterProvider -import com.pinterest.ktlint.reporter.checkstyle.CheckStyleReporterProvider -import com.pinterest.ktlint.reporter.html.HtmlReporterProvider -import com.pinterest.ktlint.reporter.json.JsonReporterProvider -import com.pinterest.ktlint.reporter.plain.Color -import com.pinterest.ktlint.reporter.plain.PlainReporterProvider -import com.pinterest.ktlint.reporter.sarif.SarifReporterProvider -import java.io.PrintStream -import java.nio.file.Paths -import kotlin.io.path.createDirectories -import kotlin.io.path.outputStream - -/** - * supported color names in __KtLint__, taken from [Color] - */ -val colorNamesForPlainReporter = Color.values().map { it.name } - -/** - * A default [ReporterProvider] for [PlainReporterProvider] - */ -val plainReporterProvider = PlainReporterProvider() - -/** - * All [ReporterProvider] which __KtLint__ provides - */ -val reporterProviders = setOf( - plainReporterProvider, - JsonReporterProvider(), - SarifReporterProvider(), - CheckStyleReporterProvider(), - HtmlReporterProvider(), -) - .associateBy { it.id } - -/** - * @return true if receiver is [PlainReporterProvider] - */ -internal fun ReporterProvider<*>.isPlain(): Boolean = id == plainReporterProvider.id - -/** - * @param reporterProviderId - * @param output - * @param colorNameInPlain - * @param groupByFileInPlain - * @param mode - * @return a configured [Reporter] - */ -fun buildReporter( - reporterProviderId: String, - output: String?, - colorNameInPlain: String?, - groupByFileInPlain: Boolean, - mode: DiktatMode, -): Reporter { - val reporterProvider = reporterProviders.getValue(reporterProviderId) - return reporterProvider.get( - out = output - ?.let { Paths.get(it) } - ?.also { it.parent.createDirectories() } - ?.outputStream() - ?.let { PrintStream(it) } - ?: System.out, - opt = buildMap { - colorNameInPlain?.let { - require(reporterProvider.isPlain()) { - "colorization is applicable only for plain reporter" - } - put("color", true) - put("color_name", it) - } ?: run { - put("color", false) - put("color_name", Color.DARK_GRAY.name) - } - put("format", (mode == DiktatMode.FIX)) - if (groupByFileInPlain) { - require(reporterProvider.isPlain()) { - "groupByFile is applicable only for plain reporter" - } - put("group_by_file", true) - } - }.mapValues { it.value.toString() }, - ) -} From 18f9be58e394cf8fecda2b27c61514dc939ae1ea Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 6 Apr 2023 15:04:07 +0300 Subject: [PATCH 23/33] self review --- .../{KtLintRuleSetWrapperTest.kt => KtLintRuleWrapperTest.kt} | 0 diktat-maven-plugin/build.gradle.kts | 1 - diktat-ruleset/src/main/resources/META-INF/version | 2 -- 3 files changed, 3 deletions(-) rename diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/{KtLintRuleSetWrapperTest.kt => KtLintRuleWrapperTest.kt} (100%) delete mode 100644 diktat-ruleset/src/main/resources/META-INF/version diff --git a/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt b/diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt similarity index 100% rename from diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleSetWrapperTest.kt rename to diktat-ktlint-engine/src/test/kotlin/org/cqfn/diktat/ktlint/KtLintRuleWrapperTest.kt diff --git a/diktat-maven-plugin/build.gradle.kts b/diktat-maven-plugin/build.gradle.kts index 3653a5372a..d7c1d0dd16 100644 --- a/diktat-maven-plugin/build.gradle.kts +++ b/diktat-maven-plugin/build.gradle.kts @@ -11,7 +11,6 @@ plugins { dependencies { implementation(libs.maven.plugin.api) - implementation(projects.diktatRuleset) compileOnly(libs.maven.plugin.annotations) compileOnly(libs.maven.core) diff --git a/diktat-ruleset/src/main/resources/META-INF/version b/diktat-ruleset/src/main/resources/META-INF/version deleted file mode 100644 index abae2e47cf..0000000000 --- a/diktat-ruleset/src/main/resources/META-INF/version +++ /dev/null @@ -1,2 +0,0 @@ -diktat: ${project.version} -ktlint: ${ktlint.version} From 7bd9a7a7472c9693cb05f29b15996698fa664d95 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 10:31:02 +0300 Subject: [PATCH 24/33] fixed Baseline loading and Color importing in plain reporter + reusing ktLintRuleEngine --- .../ktlint/DiktatBaselineFactoryImpl.kt | 9 ++-- .../ktlint/DiktatProcessorFactoryImpl.kt | 47 +++++++++---------- .../ktlint/DiktatReporterFactoryImpl.kt | 2 +- .../org/cqfn/diktat/util/FixTestBase.kt | 8 ---- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt index de461702f4..e0898075c6 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt @@ -6,7 +6,8 @@ import org.cqfn.diktat.api.DiktatProcessorListener import org.cqfn.diktat.api.DiktatProcessorListener.Companion.closeAfterAllAsProcessorListener import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap import org.cqfn.diktat.ktlint.DiktatReporterImpl.Companion.wrap -import com.pinterest.ktlint.core.internal.loadBaseline +import com.pinterest.ktlint.core.api.Baseline +import com.pinterest.ktlint.core.api.loadBaseline import com.pinterest.ktlint.reporter.baseline.BaselineReporter import java.io.PrintStream import java.nio.file.Path @@ -16,17 +17,15 @@ import kotlin.io.path.outputStream /** * A factory to create or generate [DiktatBaseline] using `KtLint` */ -@Suppress("DEPRECATION") class DiktatBaselineFactoryImpl : DiktatBaselineFactory { override fun tryToLoad( baselineFile: Path, sourceRootDir: Path, ): DiktatBaseline? = loadBaseline(baselineFile.absolutePathString()) - .takeUnless { it.baselineGenerationNeeded } + .takeUnless { it.status == Baseline.Status.VALID } ?.let { ktLintBaseline -> DiktatBaseline { file -> - ktLintBaseline.baselineRules - ?.get(file.relativePathStringTo(sourceRootDir)) + ktLintBaseline.lintErrorsPerFile[file.relativePathStringTo(sourceRootDir)] .orEmpty() .map { it.wrap() } .toSet() diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt index 49bfc33ca6..d481e6d654 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt @@ -4,7 +4,7 @@ import org.cqfn.diktat.DiktatProcessor import org.cqfn.diktat.DiktatProcessorFactory import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.w +import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint import com.pinterest.ktlint.core.Code import com.pinterest.ktlint.core.KtLintRuleEngine @@ -18,32 +18,31 @@ private typealias LintCallback = (LintError) -> Unit * A factory to create [DiktatProcessor] using [DiktatProcessorFactory] and `KtLint` as engine */ class DiktatProcessorFactoryImpl : DiktatProcessorFactory { - override fun invoke(diktatRuleSet: DiktatRuleSet): DiktatProcessor = object : DiktatProcessor { - override fun fix(file: Path, callback: DiktatCallback): String = - ktlintEngine(diktatRuleSet).format(file.toKtLint(), callback.toKtLintForFormat()) - override fun fix( - code: String, - isScript: Boolean, - callback: DiktatCallback - ): String = - ktlintEngine(diktatRuleSet).format(code.toKtLint(isScript), callback.toKtLintForFormat()) - override fun check(file: Path, callback: DiktatCallback) = - ktlintEngine(diktatRuleSet).lint(file.toKtLint(), callback.toKtLintForLint()) - override fun check( - code: String, - isScript: Boolean, - callback: DiktatCallback - ) = - ktlintEngine(diktatRuleSet).lint(code.toKtLint(isScript), callback.toKtLintForLint()) + override fun invoke(diktatRuleSet: DiktatRuleSet): DiktatProcessor { + val ktLintRuleEngine = diktatRuleSet.toKtLintEngine() + return object : DiktatProcessor { + override fun fix(file: Path, callback: DiktatCallback): String = + ktLintRuleEngine.format(file.toKtLint(), callback.toKtLintForFormat()) + override fun fix( + code: String, + isScript: Boolean, + callback: DiktatCallback + ): String = + ktLintRuleEngine.format(code.toKtLint(isScript), callback.toKtLintForFormat()) + override fun check(file: Path, callback: DiktatCallback) = + ktLintRuleEngine.lint(file.toKtLint(), callback.toKtLintForLint()) + override fun check( + code: String, + isScript: Boolean, + callback: DiktatCallback + ) = + ktLintRuleEngine.lint(code.toKtLint(isScript), callback.toKtLintForLint()) + } } - private fun ktlintEngine( - diktatRuleSet: DiktatRuleSet, - ): KtLintRuleEngine = KtLintRuleEngine( - ruleProviders = diktatRuleSet.toKtLint() - ) - companion object { + private fun DiktatRuleSet.toKtLintEngine(): KtLintRuleEngine = KtLintRuleEngine(ruleProviders = toKtLint()) + private fun Path.toKtLint(): Code = Code.CodeFile(this.toFile()) private fun String.toKtLint(isScript: Boolean): Code = Code.CodeSnippet(this, isScript) diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatReporterFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatReporterFactoryImpl.kt index 96425526d1..a47f2cec8b 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatReporterFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatReporterFactoryImpl.kt @@ -7,8 +7,8 @@ import com.pinterest.ktlint.core.ReporterProvider import com.pinterest.ktlint.reporter.checkstyle.CheckStyleReporterProvider import com.pinterest.ktlint.reporter.html.HtmlReporterProvider import com.pinterest.ktlint.reporter.json.JsonReporterProvider +import com.pinterest.ktlint.reporter.plain.Color import com.pinterest.ktlint.reporter.plain.PlainReporterProvider -import com.pinterest.ktlint.reporter.plain.internal.Color import com.pinterest.ktlint.reporter.sarif.SarifReporterProvider import java.io.OutputStream import java.io.PrintStream diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index 08565fe338..177e5d6095 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -105,12 +105,4 @@ open class FixTestBase( log.warn { "Received linting error: $error" } } } - - companion object { - private val log = KotlinLogging.logger { } - - private val defaultCallback = DiktatCallback { error, _ -> - log.warn { "Received linting error: $error" } - } - } } From cb848c80d73dd51cf2674d15fae28310a117dfa8 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 10:32:22 +0300 Subject: [PATCH 25/33] fixed condition in loading baseline --- .../kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt index e0898075c6..d3fb2cedc1 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatBaselineFactoryImpl.kt @@ -22,7 +22,7 @@ class DiktatBaselineFactoryImpl : DiktatBaselineFactory { baselineFile: Path, sourceRootDir: Path, ): DiktatBaseline? = loadBaseline(baselineFile.absolutePathString()) - .takeUnless { it.status == Baseline.Status.VALID } + .takeIf { it.status == Baseline.Status.VALID } ?.let { ktLintBaseline -> DiktatBaseline { file -> ktLintBaseline.lintErrorsPerFile[file.relativePathStringTo(sourceRootDir)] From bc9b330c8d1c7c9282598afbd354c8c5cef77a93 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 10:36:13 +0300 Subject: [PATCH 26/33] formatting --- .../ktlint/DiktatProcessorFactoryImpl.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt index d481e6d654..8a3dc4e53f 100644 --- a/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt +++ b/diktat-ktlint-engine/src/main/kotlin/org/cqfn/diktat/ktlint/DiktatProcessorFactoryImpl.kt @@ -21,22 +21,24 @@ class DiktatProcessorFactoryImpl : DiktatProcessorFactory { override fun invoke(diktatRuleSet: DiktatRuleSet): DiktatProcessor { val ktLintRuleEngine = diktatRuleSet.toKtLintEngine() return object : DiktatProcessor { - override fun fix(file: Path, callback: DiktatCallback): String = - ktLintRuleEngine.format(file.toKtLint(), callback.toKtLintForFormat()) + override fun fix( + file: Path, + callback: DiktatCallback, + ): String = ktLintRuleEngine.format(file.toKtLint(), callback.toKtLintForFormat()) override fun fix( code: String, isScript: Boolean, callback: DiktatCallback - ): String = - ktLintRuleEngine.format(code.toKtLint(isScript), callback.toKtLintForFormat()) - override fun check(file: Path, callback: DiktatCallback) = - ktLintRuleEngine.lint(file.toKtLint(), callback.toKtLintForLint()) + ): String = ktLintRuleEngine.format(code.toKtLint(isScript), callback.toKtLintForFormat()) + override fun check( + file: Path, + callback: DiktatCallback, + ) = ktLintRuleEngine.lint(file.toKtLint(), callback.toKtLintForLint()) override fun check( code: String, isScript: Boolean, callback: DiktatCallback - ) = - ktLintRuleEngine.lint(code.toKtLint(isScript), callback.toKtLintForLint()) + ) = ktLintRuleEngine.lint(code.toKtLint(isScript), callback.toKtLintForLint()) } } From 4630fc65c49900fa40b8ba8f885153165f6ec463 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 12:23:22 +0300 Subject: [PATCH 27/33] fixed file path --- diktat-rules/build.gradle.kts | 15 ++++++++++-- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 24 ++++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/diktat-rules/build.gradle.kts b/diktat-rules/build.gradle.kts index 066881e477..aa61b807af 100644 --- a/diktat-rules/build.gradle.kts +++ b/diktat-rules/build.gradle.kts @@ -36,8 +36,19 @@ dependencies { } kotlin { - sourceSets.main { - kotlin.srcDir("build/generated/ksp/main/kotlin") + sourceSets { + main { + kotlin.srcDir("build/generated/ksp/main/kotlin") + } + test { + kotlin.srcDir("build/generated/ksp/test/kotlin") + } + } +} + +project.afterEvaluate { + tasks.named("compileTestKotlin") { + dependsOn(tasks.named("kspKotlin")) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 3b57fae26a..6b5fa05e0e 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -803,16 +803,22 @@ fun ASTNode.findAllNodesWithConditionOnLine( * * @return name of the file [this] node belongs to */ -fun ASTNode.getFilePath(): String = getRootNode() - .takeIf { - it.elementType == FILE - } - ?.psi - ?.let { it as? KtFile } - ?.name - .let { - requireNotNull(it) { "Root node type is not FILE, but only ${KtFile::class} has file name" } +fun ASTNode.getFilePath(): String = run { +// It doesn't work since, KtLint create KtFile using file name, not a file path +// getRootNode().psi +// ?.let { it as? KtFile } +// ?.name +// .let { +// requireNotNull(it) { "Root node type is not FILE, but only ${KtFile::class} has file name" } +// } + @Suppress("Deprecation") + val key = com.pinterest.ktlint.core.KtLint.FILE_PATH_USER_DATA_KEY + getRootNode().also { + require(it.elementType == FILE) { "Root node type is not FILE, but $key is present in user_data only in FILE nodes" } + }.getUserData(key).let { + requireNotNull(it) { "File path is not present in user data" } } +} /** * checks that this one node is placed after the other node in code (by comparing lines of code where nodes start) From 57a1c340ba8a2a6ea3d9b673257c69a26c0bde62 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 12:44:00 +0300 Subject: [PATCH 28/33] fixed file path for snippets --- diktat-rules/build.gradle.kts | 26 ------------------ .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/diktat-rules/build.gradle.kts b/diktat-rules/build.gradle.kts index aa61b807af..6965e888a6 100644 --- a/diktat-rules/build.gradle.kts +++ b/diktat-rules/build.gradle.kts @@ -34,29 +34,3 @@ dependencies { ksp(projects.diktatDevKsp) testImplementation(libs.kotlin.reflect) } - -kotlin { - sourceSets { - main { - kotlin.srcDir("build/generated/ksp/main/kotlin") - } - test { - kotlin.srcDir("build/generated/ksp/test/kotlin") - } - } -} - -project.afterEvaluate { - tasks.named("compileTestKotlin") { - dependsOn(tasks.named("kspKotlin")) - } -} - -idea { - module { - // Not using += due to https://github.com/gradle/gradle/issues/8749 - sourceDirs = sourceDirs + file("build/generated/ksp/main/kotlin") // or tasks["kspKotlin"].destination - testSourceDirs = testSourceDirs + file("build/generated/ksp/test/kotlin") - generatedSourceDirs = generatedSourceDirs + file("build/generated/ksp/main/kotlin") + file("build/generated/ksp/test/kotlin") - } -} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 6b5fa05e0e..e15e4899ed 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -71,6 +71,7 @@ import org.jetbrains.kotlin.psi.KtParameterList import org.jetbrains.kotlin.psi.psiUtil.children import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.psi.stubs.elements.KtFileElementType import java.util.Locale @@ -804,20 +805,22 @@ fun ASTNode.findAllNodesWithConditionOnLine( * @return name of the file [this] node belongs to */ fun ASTNode.getFilePath(): String = run { -// It doesn't work since, KtLint create KtFile using file name, not a file path -// getRootNode().psi -// ?.let { it as? KtFile } -// ?.name -// .let { -// requireNotNull(it) { "Root node type is not FILE, but only ${KtFile::class} has file name" } -// } @Suppress("Deprecation") val key = com.pinterest.ktlint.core.KtLint.FILE_PATH_USER_DATA_KEY - getRootNode().also { - require(it.elementType == FILE) { "Root node type is not FILE, but $key is present in user_data only in FILE nodes" } - }.getUserData(key).let { - requireNotNull(it) { "File path is not present in user data" } - } + val rootNode = getRootNode() + .also { + require(it.elementType == KtFileElementType.INSTANCE) { "Root node type is not FILE, but $key can present in user_data only in FILE nodes" } + } + + rootNode.getUserData(key) + ?: run { + // KtLint doesn't set file path for snippets + // will take a file name from KtFile + // it doesn't work for all cases since KtLint creates KtFile using a file name, not a file path + requireNotNull(rootNode.let { it as? KtFile }) { + "Root node type is not ${KtFile::class}" + }.name + } } /** From 6d59816ae8e6cb69eaff5b1185c476d495884cd2 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 12:53:01 +0300 Subject: [PATCH 29/33] fixed getting KtFile --- diktat-rules/build.gradle.kts | 10 ++++++++++ .../org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/diktat-rules/build.gradle.kts b/diktat-rules/build.gradle.kts index 6965e888a6..80af533fec 100644 --- a/diktat-rules/build.gradle.kts +++ b/diktat-rules/build.gradle.kts @@ -34,3 +34,13 @@ dependencies { ksp(projects.diktatDevKsp) testImplementation(libs.kotlin.reflect) } + +project.afterEvaluate { + tasks.named("kspKotlin") { + // not clear issue that :kspKotlin is up-to-date, but generated files are missed + outputs.upToDateWhen { false } + } + tasks.named("test") { + dependsOn(tasks.named("kspKotlin")) + } +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index e15e4899ed..3f1733a3b2 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -817,7 +817,7 @@ fun ASTNode.getFilePath(): String = run { // KtLint doesn't set file path for snippets // will take a file name from KtFile // it doesn't work for all cases since KtLint creates KtFile using a file name, not a file path - requireNotNull(rootNode.let { it as? KtFile }) { + requireNotNull(rootNode.psi as? KtFile) { "Root node type is not ${KtFile::class}" }.name } From eb6406bbffc8fd8cd9d1fe2e11016ee14408ff53 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 13:51:12 +0300 Subject: [PATCH 30/33] fixed Smoke tests --- .../kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 1 + .../org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt | 2 +- diktat-ruleset/src/test/resources/test/smoke/save.toml | 2 +- .../test/resources/test/smoke/src/main/kotlin/save.toml | 2 +- .../diktat/test/framework/processing/TestComparatorUnit.kt | 3 ++- .../org/cqfn/diktat/test/framework/util/TestUtils.kt | 7 ++++++- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 3f1733a3b2..a6f351a458 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -817,6 +817,7 @@ fun ASTNode.getFilePath(): String = run { // KtLint doesn't set file path for snippets // will take a file name from KtFile // it doesn't work for all cases since KtLint creates KtFile using a file name, not a file path + // raised: https://github.com/pinterest/ktlint/issues/1921 requireNotNull(rootNode.psi as? KtFile) { "Root node type is not ${KtFile::class}" }.name diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt index b71a3baa5a..6787c56072 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt @@ -238,8 +238,8 @@ abstract class DiktatSmokeTestBase { /* * This 2nd `MISSING_KDOC_ON_FUNCTION` is a duplicate caused by * https://github.com/saveourtool/diktat/issues/1538. - * LintError(6, 5, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", "${MISSING_KDOC_ON_FUNCTION.warnText()} foo", false), */ + LintError(6, 5, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", "${MISSING_KDOC_ON_FUNCTION.warnText()} foo", false), LintError(9, 3, "$DIKTAT_RULE_SET_ID:${EmptyBlock.NAME_ID}", EMPTY_BLOCK_STRUCTURE_ERROR.warnText() + " empty blocks are forbidden unless it is function with override keyword", false), LintError(12, 10, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), diff --git a/diktat-ruleset/src/test/resources/test/smoke/save.toml b/diktat-ruleset/src/test/resources/test/smoke/save.toml index ba0e3acee4..8a38cfd61d 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/save.toml @@ -1,5 +1,5 @@ [general] -execCmd="java -showversion -jar ktlint --debug --verbose -R diktat.jar" +execCmd="java -showversion -jar ktlint --log-level=debug -R diktat.jar" tags = ["smokeTest"] description = "SmokeTest" suiteName = "SmokeTest" diff --git a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml index f4d8eb501e..032557dfd1 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml @@ -1,5 +1,5 @@ [general] -execCmd="java -showversion -jar ktlint --debug --verbose -R diktat.jar" +execCmd="java -showversion -jar ktlint --log-level=debug -R diktat.jar" tags = ["smokeTest"] description = "SmokeTest" suiteName = "SmokeTest" diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt index 077a809850..4b1c9ccb10 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt @@ -1,6 +1,7 @@ package org.cqfn.diktat.test.framework.processing import org.cqfn.diktat.test.framework.util.readTextOrNull +import org.cqfn.diktat.test.framework.util.toUnixEndLines import mu.KotlinLogging import java.nio.file.Path import kotlin.io.path.isRegularFile @@ -77,7 +78,7 @@ class TestComparatorUnit( expectedContent = "// $expectedFile is a regular file: ${expectedFile.isRegularFile()}") } - val actualFileContent = function(testFile) + val actualFileContent = function(testFile).toUnixEndLines() val expectedFileContent = expectedFile.readTextOrNull().orEmpty() val comparator = FileComparator( diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt index 5f8b1d31a0..bda46c5714 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt @@ -274,13 +274,18 @@ fun String?.isWindows(): Boolean { return this != null && startsWith("Windows") } +/** + * @return original [String] with unix end lines + */ +fun String.toUnixEndLines(): String = replace("\r\n", "\n").replace("\r", "\n") + /** * @receiver the file whose content is to be read. * @return file content as a single [String], or null if an I/O error * has occurred. */ fun Path.readTextOrNull(): String? = try { - readText(StandardCharsets.UTF_8).replace("\r\n", "\n").replace("\r", "\n") + readText(StandardCharsets.UTF_8).toUnixEndLines() } catch (e: IOException) { logger.error(e) { "Not able to read file: $this" } null From 7ec85c43c8d6b5080e6d82f9cf053960d1d7f3aa Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 14:32:19 +0300 Subject: [PATCH 31/33] disabled ktlint's rule properly --- .editorconfig | 9 +++++++++ .github/workflows/build_and_test.yml | 2 +- diktat-ruleset/build.gradle.kts | 3 +++ .../src/test/resources/test/smoke/.editorconfig | 8 ++++++++ diktat-ruleset/src/test/resources/test/smoke/save.toml | 2 +- .../test/resources/test/smoke/src/main/kotlin/save.toml | 2 +- 6 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 diktat-ruleset/src/test/resources/test/smoke/.editorconfig diff --git a/.editorconfig b/.editorconfig index 5f30b45a1d..d8cc0b33b1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,6 @@ +# https://editorconfig.org +root = true + [*] charset = utf-8 indent_size = 4 @@ -99,3 +102,9 @@ ij_kotlin_while_on_new_line = false ij_kotlin_wrap_elvis_expressions = 1 ij_kotlin_wrap_expression_body_functions = 1 ij_kotlin_wrap_first_method_in_call_chain = false + +# disable ktlint rules +ktlint_standard = disabled +ktlint_experimental = disabled +ktlint_test = disabled +ktlint_custom = disabled diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5042ca3eb8..4f2a568288 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -113,7 +113,7 @@ jobs: if: matrix.type == 'ktlint' run: | filename=$(ls -1 diktat-*.jar | head -n1) - echo DIKTAT_RUN="java -jar ktlint -R \"$filename\" --disabled_rules=standard,experimental,test,custom" >> $GITHUB_ENV + echo DIKTAT_RUN="java -jar ktlint -R \"$filename\" >> $GITHUB_ENV shell: bash - name: Download diktat cli jar diff --git a/diktat-ruleset/build.gradle.kts b/diktat-ruleset/build.gradle.kts index b1b72bbbca..b0c1d2b58d 100644 --- a/diktat-ruleset/build.gradle.kts +++ b/diktat-ruleset/build.gradle.kts @@ -60,6 +60,9 @@ tasks { build { dependsOn(shadowJar) } + test { + dependsOn(shadowJar) + } } publishing { diff --git a/diktat-ruleset/src/test/resources/test/smoke/.editorconfig b/diktat-ruleset/src/test/resources/test/smoke/.editorconfig new file mode 100644 index 0000000000..f085180344 --- /dev/null +++ b/diktat-ruleset/src/test/resources/test/smoke/.editorconfig @@ -0,0 +1,8 @@ +# https://editorconfig.org +root = true + +[*.{kt,kts}] +ktlint_standard = disabled +ktlint_experimental = disabled +ktlint_test = disabled +ktlint_custom = disabled diff --git a/diktat-ruleset/src/test/resources/test/smoke/save.toml b/diktat-ruleset/src/test/resources/test/smoke/save.toml index 8a38cfd61d..f5bcdde6a3 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/save.toml @@ -9,7 +9,7 @@ timeOutMillis = 3600000 ["fix and warn"] ["fix and warn".fix] - execFlags="--disabled_rules=standard,experimental,test,custom -F" + execFlags="-F" ["fix and warn".warn] lineCaptureGroup = 1 columnCaptureGroup = 2 diff --git a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml index 032557dfd1..467f9d120d 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml @@ -8,7 +8,7 @@ expectedWarningsPattern = "// ;warn:?(.*):(\\d*): (.+)" ["fix and warn"] ["fix and warn".fix] - execFlags="--disabled_rules=standard,experimental,test,custom -F" + execFlags="-F" ["fix and warn".warn] actualWarningsPattern = "(\\w+\\..+):(\\d+):(\\d+): (\\[.*\\].*)$" exactWarningsMatch = false From 6a4bfcf6b68ad820c56f114f188128cc9df9237e Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 15:03:41 +0300 Subject: [PATCH 32/33] temp directory for Unix in the same way as for Windows --- .../diktat/ruleset/smoke/DiktatSaveSmokeTest.kt | 14 ++++++-------- .../src/test/resources/test/smoke/.editorconfig | 6 +++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt index bc8e445b07..95129883e0 100644 --- a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt @@ -78,12 +78,12 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { * On Windows, ktlint is often unable to relativize paths * (see https://github.com/pinterest/ktlint/issues/1608). * + * Also, ktlint needs `.editorconfig` to disable standard rules + * * So let's force the temporary directory to be the * sub-directory of the project root. */ - if (System.getProperty("os.name").isWindows()) { - temporaryDirectory(baseDirectoryPath / WINDOWS_TEMP_DIRECTORY) - } + temporaryDirectory(baseDirectoryPath / TEMP_DIRECTORY) } val saveProcess = processBuilder.start() @@ -131,7 +131,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { private val logger = KotlinLogging.logger {} private const val BASE_DIRECTORY = "src/test/resources/test/smoke" private const val SAVE_VERSION: String = "0.3.4" - private const val WINDOWS_TEMP_DIRECTORY = ".save-cli" + private const val TEMP_DIRECTORY = ".save-cli" private val baseDirectoryPath = Path(BASE_DIRECTORY).absolute() private fun getSaveForCurrentOs(): String { @@ -191,15 +191,13 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { val diktat = baseDirectoryPath / DIKTAT_FAT_JAR val save = baseDirectoryPath / getSaveForCurrentOs() val ktlint = baseDirectoryPath / KTLINT_FAT_JAR + val tempDirectory = baseDirectoryPath / TEMP_DIRECTORY diktat.deleteIfExistsSilently() ktlint.deleteIfExistsSilently() save.deleteIfExistsSilently() - if (System.getProperty("os.name").isWindows()) { - val tempDirectory = baseDirectoryPath / WINDOWS_TEMP_DIRECTORY - tempDirectory.deleteIfExistsRecursively() - } + tempDirectory.deleteIfExistsRecursively() } } } diff --git a/diktat-ruleset/src/test/resources/test/smoke/.editorconfig b/diktat-ruleset/src/test/resources/test/smoke/.editorconfig index f085180344..475d1b02b7 100644 --- a/diktat-ruleset/src/test/resources/test/smoke/.editorconfig +++ b/diktat-ruleset/src/test/resources/test/smoke/.editorconfig @@ -1,8 +1,8 @@ # https://editorconfig.org root = true - -[*.{kt,kts}] +[{*.kt,*.kts}] +# disable ktlint rules ktlint_standard = disabled ktlint_experimental = disabled ktlint_test = disabled -ktlint_custom = disabled +ktlint_custom = disabled \ No newline at end of file From 128cf9f17e3d30f682b7b9520338314b2aa9a02d Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 7 Apr 2023 15:14:53 +0300 Subject: [PATCH 33/33] fixed build_and_test.yml --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 4f2a568288..ae65e84100 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -113,7 +113,7 @@ jobs: if: matrix.type == 'ktlint' run: | filename=$(ls -1 diktat-*.jar | head -n1) - echo DIKTAT_RUN="java -jar ktlint -R \"$filename\" >> $GITHUB_ENV + echo DIKTAT_RUN="java -jar ktlint -R \"$filename\"" >> $GITHUB_ENV shell: bash - name: Download diktat cli jar