From a8cb51e092787a03a6eb27fbe2788a0b07a426cf Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 7 Jun 2023 17:19:02 +0300 Subject: [PATCH 01/19] Diktat runner ### What's done: - main runner It closes #1685 --- .../org/cqfn/diktat/DiktatMainRunner.kt | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt diff --git a/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt b/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt new file mode 100644 index 0000000000..5559ee6f76 --- /dev/null +++ b/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt @@ -0,0 +1,66 @@ +package org.cqfn.diktat + +import org.cqfn.diktat.api.DiktatRuleSet +import org.cqfn.diktat.api.DiktatRuleSetFactory +import org.cqfn.diktat.ktlint.DiktatProcessorFactoryImpl +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl +import mu.KotlinLogging +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.core.LoggerContext +import org.slf4j.event.Level + +private val log = KotlinLogging.logger { } + +fun main() { + // a temporary + val logLevel = Level.ERROR + // 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() + + // default implementations + val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() + val diktatProcessorFactory = DiktatProcessorFactoryImpl() + + // ruleSet + val diktatRuleSet: DiktatRuleSet = diktatRuleSetFactory() + val diktatProcessor: DiktatProcessor = diktatProcessorFactory(diktatRuleSet) + + val code = mutableListOf() + while (true) { + val line = readln() + if (line == "CHECK") { + diktatProcessor.check( + code = code.joinToString(), + isScript = true + ) { error, _ -> + println(error.toString()) + } + code.clear() + } else if (line == "FIX") { + val result = diktatProcessor.fix( + code = code.joinToString(), + isScript = true + ) { error, _ -> + println(error.toString()) + } + code.clear() + println("Formatted code:") + println(result) + } else if (line == "END") { + break + } else { + code.add(line) + } + } +} From 493c83edf2586bbb04c6f8ab39d1dc51bc8cbe09 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 7 Jun 2023 18:25:22 +0300 Subject: [PATCH 02/19] WIP --- .../org/cqfn/diktat/DiktatRunnerFactory.kt | 5 +- .../org/cqfn/diktat/api/DiktatRuleConfig.kt | 16 ++ .../cqfn/diktat/api/DiktatRuleConfigReader.kt | 14 ++ .../cqfn/diktat/api/DiktatRuleSetFactory.kt | 5 +- diktat-common/build.gradle.kts | 1 + .../config/reader/AbstractConfigReader.kt | 39 ++++ ...der.kt => AbstractResourceConfigReader.kt} | 31 +-- .../common/config/rules/RulesConfigReader.kt | 38 ++-- .../rules/DiktatRuleConfigReaderImpl.kt | 90 ++++++++ .../ruleset/rules/DiktatRuleSetFactoryImpl.kt | 196 +++++++++++++++++- .../rules/chapter2/kdoc/KdocComments.kt | 2 - .../rules/chapter3/files/FileStructureRule.kt | 1 - .../paragraph3/newlines/SemicolonsExpected.kt | 4 +- .../paragraph3/newlines/SemicolonsTest.kt | 4 +- .../framework/config/TestArgumentsReader.kt | 20 +- .../test/framework/config/TestConfigReader.kt | 20 +- 16 files changed, 402 insertions(+), 84 deletions(-) create mode 100644 diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt create mode 100644 diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt create mode 100644 diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt rename diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/{JsonResourceConfigReader.kt => AbstractResourceConfigReader.kt} (61%) create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt index 7e886c787c..726c559529 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt @@ -6,6 +6,7 @@ import org.cqfn.diktat.api.DiktatProcessorListener import org.cqfn.diktat.api.DiktatProcessorListener.Companion.closeAfterAllAsProcessorListener import org.cqfn.diktat.api.DiktatReporter import org.cqfn.diktat.api.DiktatReporterFactory +import org.cqfn.diktat.api.DiktatRuleConfigReader import org.cqfn.diktat.api.DiktatRuleSetFactory import java.io.OutputStream import java.nio.file.Path @@ -16,6 +17,7 @@ import java.nio.file.Path * @property diktatReporterFactory a factory for [DiktatReporter] */ class DiktatRunnerFactory( + private val diktatRuleConfigReader: DiktatRuleConfigReader, private val diktatRuleSetFactory: DiktatRuleSetFactory, private val diktatProcessorFactory: DiktatProcessorFactory, private val diktatBaselineFactory: DiktatBaselineFactory, @@ -26,7 +28,8 @@ class DiktatRunnerFactory( * @return an instance of [DiktatRunner] created using [args] */ override fun invoke(args: DiktatRunnerArguments): DiktatRunner { - val diktatRuleSet = diktatRuleSetFactory.create(args.configFileName) + val diktatRuleConfigs = diktatRuleConfigReader(args.configFileName) + val diktatRuleSet = diktatRuleSetFactory create(args.configFileName) val processor = diktatProcessorFactory(diktatRuleSet) val (baseline, baselineGenerator) = resolveBaseline(args.baselineFile, args.sourceRootDir) val (reporter, closer) = resolveReporter( diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt new file mode 100644 index 0000000000..3c93cd675d --- /dev/null +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt @@ -0,0 +1,16 @@ +package org.cqfn.diktat.api + +/** + * Configuration of individual [DiktatRule] + * + * @property name name of the rule + * @property enabled + * @property configuration a map of strings with configuration options + * @property ignoreAnnotated if a code block is marked with these annotations - it will not be checked by this rule + */ +data class DiktatRuleConfig( + val name: String, + val enabled: Boolean = true, + val configuration: Map = emptyMap(), + val ignoreAnnotated: Set = emptySet(), +) diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt new file mode 100644 index 0000000000..77eb233de1 --- /dev/null +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt @@ -0,0 +1,14 @@ +package org.cqfn.diktat.api + +import java.io.InputStream + +/** + * A reader for [DiktatRuleConfig] + */ +interface DiktatRuleConfigReader : Function1> { + /** + * @param inputStream + * @return parsed [DiktatRuleConfig]s + */ + override operator fun invoke(inputStream: InputStream): List +} diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt index 5f66e87e64..674f934175 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt @@ -6,11 +6,12 @@ import kotlin.io.path.absolutePathString /** * A factory which creates a [DiktatRuleSet]. */ -interface DiktatRuleSetFactory : Function0 { +interface DiktatRuleSetFactory : Function1, DiktatRuleSet> { /** + * @param rulesConfig all configurations for rules * @return the default instance of [DiktatRuleSet] */ - override operator fun invoke(): DiktatRuleSet + override operator fun invoke(rulesConfig: List): DiktatRuleSet /** * @param configFile a path to file with configuration for diktat (`diktat-analysis.yml`) diff --git a/diktat-common/build.gradle.kts b/diktat-common/build.gradle.kts index a4215a0a42..024395022b 100644 --- a/diktat-common/build.gradle.kts +++ b/diktat-common/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { api(libs.kaml) implementation(libs.apache.commons.cli) implementation(libs.kotlin.logging) + implementation(projects.diktatApi) testImplementation(libs.junit.jupiter) testImplementation(libs.assertj.core) } diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt new file mode 100644 index 0000000000..ed056830db --- /dev/null +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt @@ -0,0 +1,39 @@ +package org.cqfn.diktat.common.config.reader + +import mu.KotlinLogging +import java.io.IOException +import java.io.InputStream + +/** + * This class is used to read input stream in any format that you will specify. + * Usage: + * 1) implement this class with implementing the method: + * a. parse - implement parser for your file format (for example parse it to a proper json) + * 2) Use your new class MyReader(javaClass.classLoader).read(someInputStream) + * + * @param - class name parameter that will be used in calculation of classpath + */ +abstract class AbstractConfigReader { + /** + * @param inputStream - input stream + * @return object of type [T] if resource has been parsed successfully + */ + fun read(inputStream: InputStream): T? = try { + parse(inputStream) + } catch (e: IOException) { + log.error("Cannot read config from input stream due to: ", e) + null + } + + /** + * you can specify your own parser, in example for parsing stream as a json + * + * @param inputStream a [InputStream] representing loaded content + * @return resource parsed as type [T] + */ + protected abstract fun parse(inputStream: InputStream): T + + companion object { + private val log = KotlinLogging.logger {} + } +} diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/JsonResourceConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt similarity index 61% rename from diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/JsonResourceConfigReader.kt rename to diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt index 6da9a9a3d3..a18200bc89 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/JsonResourceConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt @@ -2,25 +2,23 @@ package org.cqfn.diktat.common.config.reader import mu.KotlinLogging -import java.io.BufferedReader import java.io.IOException +import java.io.InputStream /** * This class is used to read some resource in any format that you will specify. * Usage: * 1) implement this class with implementing two methods: * a. getConfigFile - to return URI (path) to the file that you want to read - * b. parseResource - implement parser for your file format (for example parse it to a proper json) + * b. parse - implement parser for your file format (for example parse it to a proper json) * 2) Use your new class MyReader(javaClass.classLoader).readResource("some/path/to/file.format") * - * @param - class name parameter that will be used in calculation of classpath + * @param classLoader The [ClassLoader] used to load the requested resource. + * @param T - class name parameter that will be used in calculation of classpath */ -abstract class JsonResourceConfigReader { - /** - * The [ClassLoader] used to load the requested resource. - */ - abstract val classLoader: ClassLoader - +abstract class AbstractResourceConfigReader( + protected val classLoader: ClassLoader +) : AbstractConfigReader() { /** * @param resourceFileName - related path to a file from resources * @return object of type [T] if resource has been parsed successfully @@ -29,7 +27,7 @@ abstract class JsonResourceConfigReader { val resourceStream = getConfigFile(resourceFileName) resourceStream?.let { try { - return parseResource(it) + return parse(it) } catch (e: IOException) { log.error("Cannot read config file $resourceFileName due to: ", e) } @@ -42,18 +40,9 @@ abstract class JsonResourceConfigReader { * you can override this method in case you would like to read a file not simply from resources * * @param resourceFileName name of the resource which will be loaded using [classLoader] - * @return [BufferedReader] representing loaded resource - */ - protected open fun getConfigFile(resourceFileName: String): BufferedReader? = - classLoader.getResourceAsStream(resourceFileName)?.bufferedReader() - - /** - * you can specify your own parser, in example for parsing stream as a json - * - * @param fileStream a [BufferedReader] representing loaded resource file - * @return resource parsed as type [T] + * @return [InputStream] representing loaded resource */ - protected abstract fun parseResource(fileStream: BufferedReader): T + protected open fun getConfigFile(resourceFileName: String): InputStream? = classLoader.getResourceAsStream(resourceFileName) companion object { private val log = KotlinLogging.logger {} diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt index 29c8ef8e02..e903bbd38c 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt @@ -4,21 +4,22 @@ package org.cqfn.diktat.common.config.rules -import org.cqfn.diktat.common.config.reader.JsonResourceConfigReader +import org.cqfn.diktat.api.DiktatRuleConfig +import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader import org.cqfn.diktat.common.config.rules.RulesConfigReader.Companion.log import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.YamlConfiguration +import com.charleskorn.kaml.decodeFromStream import mu.KLogger import mu.KotlinLogging -import java.io.BufferedReader import java.io.File +import java.io.InputStream import java.util.Locale import java.util.concurrent.atomic.AtomicInteger import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString /** * Name of common configuration @@ -48,20 +49,7 @@ interface Rule { fun ruleName(): String } -/** - * Configuration of individual [Rule] - * @property name name of the rule - * @property enabled - * @property configuration a map of strings with configuration options - * @property ignoreAnnotated if a code block is marked with this annotations - it will not be checked by this rule - */ -@Serializable -data class RulesConfig( - val name: String, - val enabled: Boolean = true, - val configuration: Map = emptyMap(), - val ignoreAnnotated: Set = emptySet(), -) +typealias RulesConfig = DiktatRuleConfig /** * Configuration that allows customizing additional options of particular rules. @@ -73,34 +61,32 @@ open class RuleConfiguration(protected val config: Map) * class returns the list of configurations that we have read from a yml: diktat-analysis.yml * @property classLoader a [ClassLoader] used to load configuration file */ -open class RulesConfigReader(override val classLoader: ClassLoader) : JsonResourceConfigReader>() { +open class RulesConfigReader(classLoader: ClassLoader) : AbstractResourceConfigReader>(classLoader) { private val yamlSerializer by lazy { Yaml(configuration = YamlConfiguration(strictMode = true)) } /** * Parse resource file into list of [RulesConfig] * - * @param fileStream a [BufferedReader] representing loaded rules config file + * @param inputStream a [InputStream] representing loaded rules config file * @return list of [RulesConfig] */ - override fun parseResource(fileStream: BufferedReader): List = fileStream.use { stream -> - yamlSerializer.decodeFromString>(stream.readLines().joinToString(separator = "\n")).reversed().distinctBy { it.name } - } + override fun parse(inputStream: InputStream): List = yamlSerializer.decodeFromStream(inputStream) /** * instead of reading the resource as it is done in the interface we will read a file by the absolute path here * if the path is provided, else will read the hardcoded file 'diktat-analysis.yml' from the package * * @param resourceFileName name of the resource which will be loaded using [classLoader] - * @return [BufferedReader] representing loaded resource + * @return [InputStream] representing loaded resource */ - override fun getConfigFile(resourceFileName: String): BufferedReader? { + override fun getConfigFile(resourceFileName: String): InputStream? { val resourceFile = File(resourceFileName) return if (resourceFile.exists()) { log.debug("Using $DIKTAT_ANALYSIS_CONF file from the following path: ${resourceFile.absolutePath}") - File(resourceFileName).bufferedReader() + File(resourceFileName).inputStream() } else { log.debug("Using the default $DIKTAT_ANALYSIS_CONF file from the class path") - classLoader.getResourceAsStream(resourceFileName)?.bufferedReader() + classLoader.getResourceAsStream(resourceFileName) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt new file mode 100644 index 0000000000..38c01cb376 --- /dev/null +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -0,0 +1,90 @@ +package org.cqfn.diktat.ruleset.rules + +import org.cqfn.diktat.api.DiktatRuleConfig +import org.cqfn.diktat.api.DiktatRuleConfigReader +import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF +import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON +import org.cqfn.diktat.common.config.rules.DIKTAT_CONF_PROPERTY +import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID +import org.cqfn.diktat.common.config.rules.RulesConfigReader +import org.cqfn.diktat.ruleset.constants.Warnings +import mu.KotlinLogging +import org.jetbrains.kotlin.org.jline.utils.Levenshtein +import java.io.File +import java.io.InputStream + +/** + * Default implementation for [DiktatRuleConfigReader] + */ +class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { + override fun invoke(inputStream: InputStream): List { + return RulesConfigReader(javaClass.classLoader) + .read(inputStream) + ?.onEach(::validate) + ?: emptyList() + } + + private fun validate(config: org.cqfn.diktat.common.config.rules.RulesConfig) = + require(config.name == DIKTAT_COMMON || config.name in Warnings.names) { + val closestMatch = Warnings.names.minByOrNull { Levenshtein.distance(it, config.name) } + "Warning name <${config.name}> in configuration file is invalid, did you mean <$closestMatch>?" + } + + companion object { + private val log = KotlinLogging.logger {} + + /** + * @param diktatConfigFile the configuration file where all configurations for + * inspections and rules are stored. + * @return resolved existed diktatConfigFile + */ + fun resolveConfigFile(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): String { + val possibleConfigs: Sequence = sequence { + yield(resolveDefaultConfig(diktatConfigFile)) + yield(resolveConfigFileFromJarLocation(diktatConfigFile)) + yield(resolveConfigFileFromSystemProperty()) + } + + log.debug("Will run $DIKTAT_RULE_SET_ID with $diktatConfigFile" + + " (it can be placed to the run directory or the default file from resources will be used)") + val configPath = possibleConfigs + .firstOrNull { it != null && File(it).exists() } + return configPath + ?: run { + val possibleConfigsList = possibleConfigs.toList() + log.warn( + "Configuration file not found in directory where diktat is run (${possibleConfigsList[0]}) " + + "or in the directory where diktat.jar is stored (${possibleConfigsList[1]}) " + + "or in system property (${possibleConfigsList[2]}), " + + "the default file included in jar will be used. " + + "Some configuration options will be disabled or substituted with defaults. " + + "Custom configuration file should be placed in diktat working directory if run from CLI " + + "or provided as configuration options in plugins." + ) + diktatConfigFile + } + } + + private fun resolveDefaultConfig(diktatConfigFile: String) = diktatConfigFile + + private fun resolveConfigFileFromJarLocation(diktatConfigFile: String): String { + // 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 = + javaClass + .protectionDomain + .codeSource + .location + .toURI() + + val configPathWithFileName = File(ruleSetProviderPath).absolutePath + + val indexOfName = configPathWithFileName.lastIndexOf(File.separator) + val configPath = if (indexOfName > -1) configPathWithFileName.substring(0, indexOfName) else configPathWithFileName + + return "$configPath${File.separator}$diktatConfigFile" + } + + private fun resolveConfigFileFromSystemProperty(): String? = System.getProperty(DIKTAT_CONF_PROPERTY) + } +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt index d4f04283ee..e06110f721 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt @@ -1,13 +1,205 @@ package org.cqfn.diktat.ruleset.rules +import org.cqfn.diktat.api.DiktatRuleConfig import org.cqfn.diktat.api.DiktatRuleSet import org.cqfn.diktat.api.DiktatRuleSetFactory +import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming +import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming +import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming +import org.cqfn.diktat.ruleset.rules.chapter2.comments.CommentsRule +import org.cqfn.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule +import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.CommentsFormatting +import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocComments +import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocFormatting +import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocMethods +import org.cqfn.diktat.ruleset.rules.chapter3.AnnotationNewLineRule +import org.cqfn.diktat.ruleset.rules.chapter3.BlockStructureBraces +import org.cqfn.diktat.ruleset.rules.chapter3.BooleanExpressionsRule +import org.cqfn.diktat.ruleset.rules.chapter3.BracesInConditionalsAndLoopsRule +import org.cqfn.diktat.ruleset.rules.chapter3.ClassLikeStructuresOrderRule +import org.cqfn.diktat.ruleset.rules.chapter3.CollapseIfStatementsRule +import org.cqfn.diktat.ruleset.rules.chapter3.ConsecutiveSpacesRule +import org.cqfn.diktat.ruleset.rules.chapter3.DebugPrintRule +import org.cqfn.diktat.ruleset.rules.chapter3.EmptyBlock +import org.cqfn.diktat.ruleset.rules.chapter3.EnumsSeparated +import org.cqfn.diktat.ruleset.rules.chapter3.LineLength +import org.cqfn.diktat.ruleset.rules.chapter3.LongNumericalValuesSeparatedRule +import org.cqfn.diktat.ruleset.rules.chapter3.MagicNumberRule +import org.cqfn.diktat.ruleset.rules.chapter3.MultipleModifiersSequence +import org.cqfn.diktat.ruleset.rules.chapter3.NullableTypeRule +import org.cqfn.diktat.ruleset.rules.chapter3.RangeConventionalRule +import org.cqfn.diktat.ruleset.rules.chapter3.SingleLineStatementsRule +import org.cqfn.diktat.ruleset.rules.chapter3.SortRule +import org.cqfn.diktat.ruleset.rules.chapter3.StringConcatenationRule +import org.cqfn.diktat.ruleset.rules.chapter3.StringTemplateFormatRule +import org.cqfn.diktat.ruleset.rules.chapter3.TrailingCommaRule +import org.cqfn.diktat.ruleset.rules.chapter3.WhenMustHaveElseRule +import org.cqfn.diktat.ruleset.rules.chapter3.files.BlankLinesRule +import org.cqfn.diktat.ruleset.rules.chapter3.files.FileSize +import org.cqfn.diktat.ruleset.rules.chapter3.files.FileStructureRule +import org.cqfn.diktat.ruleset.rules.chapter3.files.IndentationRule +import org.cqfn.diktat.ruleset.rules.chapter3.files.NewlinesRule +import org.cqfn.diktat.ruleset.rules.chapter3.files.TopLevelOrderRule +import org.cqfn.diktat.ruleset.rules.chapter3.files.WhiteSpaceRule +import org.cqfn.diktat.ruleset.rules.chapter3.identifiers.LocalVariablesRule +import org.cqfn.diktat.ruleset.rules.chapter4.ImmutableValNoVarRule +import org.cqfn.diktat.ruleset.rules.chapter4.NullChecksRule +import org.cqfn.diktat.ruleset.rules.chapter4.SmartCastRule +import org.cqfn.diktat.ruleset.rules.chapter4.TypeAliasRule +import org.cqfn.diktat.ruleset.rules.chapter4.VariableGenericTypeDeclarationRule +import org.cqfn.diktat.ruleset.rules.chapter4.calculations.AccurateCalculationsRule +import org.cqfn.diktat.ruleset.rules.chapter5.AsyncAndSyncRule +import org.cqfn.diktat.ruleset.rules.chapter5.AvoidNestedFunctionsRule +import org.cqfn.diktat.ruleset.rules.chapter5.CheckInverseMethodRule +import org.cqfn.diktat.ruleset.rules.chapter5.CustomLabel +import org.cqfn.diktat.ruleset.rules.chapter5.FunctionArgumentsSize +import org.cqfn.diktat.ruleset.rules.chapter5.FunctionLength +import org.cqfn.diktat.ruleset.rules.chapter5.LambdaLengthRule +import org.cqfn.diktat.ruleset.rules.chapter5.LambdaParameterOrder +import org.cqfn.diktat.ruleset.rules.chapter5.NestedFunctionBlock +import org.cqfn.diktat.ruleset.rules.chapter5.OverloadingArgumentsFunction +import org.cqfn.diktat.ruleset.rules.chapter5.ParameterNameInOuterLambdaRule +import org.cqfn.diktat.ruleset.rules.chapter6.AvoidEmptyPrimaryConstructor +import org.cqfn.diktat.ruleset.rules.chapter6.AvoidUtilityClass +import org.cqfn.diktat.ruleset.rules.chapter6.CustomGetterSetterRule +import org.cqfn.diktat.ruleset.rules.chapter6.ExtensionFunctionsInFileRule +import org.cqfn.diktat.ruleset.rules.chapter6.ExtensionFunctionsSameNameRule +import org.cqfn.diktat.ruleset.rules.chapter6.ImplicitBackingPropertyRule +import org.cqfn.diktat.ruleset.rules.chapter6.PropertyAccessorFields +import org.cqfn.diktat.ruleset.rules.chapter6.RunInScript +import org.cqfn.diktat.ruleset.rules.chapter6.TrivialPropertyAccessors +import org.cqfn.diktat.ruleset.rules.chapter6.UseLastIndex +import org.cqfn.diktat.ruleset.rules.chapter6.UselessSupertype +import org.cqfn.diktat.ruleset.rules.chapter6.classes.AbstractClassesRule +import org.cqfn.diktat.ruleset.rules.chapter6.classes.CompactInitialization +import org.cqfn.diktat.ruleset.rules.chapter6.classes.DataClassesRule +import org.cqfn.diktat.ruleset.rules.chapter6.classes.InlineClassesRule +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 /** - * A default implementation of [DiktatRuleSetFactory] + * _KtLint_-agnostic factory which creates a [DiktatRuleSet]. + * + * 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. */ class DiktatRuleSetFactoryImpl : DiktatRuleSetFactory { - override fun invoke(): DiktatRuleSet = DiktatRuleSetProvider().invoke() + /** + * 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 [com.pinterest.ktlint.core.KtLintRuleEngine.lint] and [com.pinterest.ktlint.core.KtLintRuleEngine.format] the [DiktatRuleSet] + * is retrieved. + * This results in new instances of each [com.pinterest.ktlint.core.Rule] for each file being + * processed. + * As of that a [com.pinterest.ktlint.core.Rule] does not need to be thread-safe. + * + * However, [com.pinterest.ktlint.core.KtLintRuleEngine.format] requires the [com.pinterest.ktlint.core.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. + * + * @return a default [DiktatRuleSet] + */ + @Suppress( + "LongMethod", + "TOO_LONG_FUNCTION", + ) + override fun invoke(rulesConfig: List): 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 = sequenceOf( + // comments & documentation + ::CommentsRule, + ::SingleConstructorRule, // this rule can add properties to a primary constructor, so should be before KdocComments + ::KdocComments, + ::KdocMethods, + ::KdocFormatting, + ::CommentsFormatting, + // naming + ::FileNaming, + ::PackageNaming, + ::IdentifierNaming, + // code structure + ::UselessSupertype, + ::ClassLikeStructuresOrderRule, + ::WhenMustHaveElseRule, + ::BracesInConditionalsAndLoopsRule, + ::EmptyBlock, + ::AvoidEmptyPrimaryConstructor, + ::TopLevelOrderRule, + ::SingleLineStatementsRule, + ::MultipleModifiersSequence, + ::TrivialPropertyAccessors, + ::CustomGetterSetterRule, + ::CompactInitialization, + // other rules + ::UseLastIndex, + ::InlineClassesRule, + ::ExtensionFunctionsInFileRule, + ::CheckInverseMethodRule, + ::StatelessClassesRule, + ::ImplicitBackingPropertyRule, + ::DataClassesRule, + ::LocalVariablesRule, + ::SmartCastRule, + ::AvoidUtilityClass, + ::PropertyAccessorFields, + ::AbstractClassesRule, + ::TrailingCommaRule, + ::SingleInitRule, + ::RangeConventionalRule, + ::DebugPrintRule, + ::CustomLabel, + ::VariableGenericTypeDeclarationRule, + ::LongNumericalValuesSeparatedRule, + ::NestedFunctionBlock, + ::AnnotationNewLineRule, + ::SortRule, + ::EnumsSeparated, + ::StringConcatenationRule, + ::StringTemplateFormatRule, + ::AccurateCalculationsRule, + ::CollapseIfStatementsRule, + ::LineLength, + ::RunInScript, + ::TypeAliasRule, + ::OverloadingArgumentsFunction, + ::FunctionLength, + ::MagicNumberRule, + ::LambdaParameterOrder, + ::FunctionArgumentsSize, + ::BlankLinesRule, + ::FileSize, + ::AsyncAndSyncRule, + ::NullableTypeRule, + ::NullChecksRule, + ::ImmutableValNoVarRule, + ::AvoidNestedFunctionsRule, + ::ExtensionFunctionsSameNameRule, + ::LambdaLengthRule, + ::BooleanExpressionsRule, + ::ParameterNameInOuterLambdaRule, + // formatting: moving blocks, adding line breaks, indentations etc. + ::BlockStructureBraces, + ::ConsecutiveSpacesRule, + ::HeaderCommentRule, + ::FileStructureRule, // this rule should be right before indentation because it should operate on already valid code + ::NewlinesRule, // newlines need to be inserted right before fixing indentation + ::WhiteSpaceRule, // this rule should be after other rules that can cause wrong spacing + ::IndentationRule, // indentation rule should be the last because it fixes formatting after all the changes done by previous rules + + ) + .map { + it(rulesConfig) + } + .toList() + return DiktatRuleSet(rules) + } override fun create(configFile: String): DiktatRuleSet = DiktatRuleSetProvider(configFile).invoke() } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt index c61b36f3d8..1e98a2ba84 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt @@ -28,9 +28,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiCommentImpl import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.kdoc.lexer.KDocTokens -import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.END import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.KDOC -import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.TEXT import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.BLOCK_COMMENT diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt index 1ab50ec75d..618ece1d10 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt @@ -37,7 +37,6 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.KDOC -import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.MARKDOWN_LINK import org.jetbrains.kotlin.lexer.KtTokens.BLOCK_COMMENT import org.jetbrains.kotlin.lexer.KtTokens.EOL_COMMENT import org.jetbrains.kotlin.lexer.KtTokens.WHITE_SPACE diff --git a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt index 669e47dc26..71e83bb27f 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt @@ -1,7 +1,5 @@ package test.paragraph3.newlines -import org.cqfn.diktat.common.config.rules.RulesConfig - enum class Example { A, B @@ -10,4 +8,4 @@ enum class Example { fun foo() {} val a = 0 val b = if (condition) { bar(); baz()} else qux -} \ No newline at end of file +} diff --git a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt index f16c4542c4..6a8385f9e0 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt @@ -1,7 +1,5 @@ package test.paragraph3.newlines; -import org.cqfn.diktat.common.config.rules.RulesConfig; - enum class Example { A, B @@ -10,4 +8,4 @@ enum class Example { fun foo() {}; val a = 0; val b = if (condition) { bar(); baz()} else qux -}; \ No newline at end of file +}; diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt index 42ad373eee..e27049ddcb 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt @@ -1,7 +1,7 @@ package org.cqfn.diktat.test.framework.config import org.cqfn.diktat.common.cli.CliArgument -import org.cqfn.diktat.common.config.reader.JsonResourceConfigReader +import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader import mu.KotlinLogging import org.apache.commons.cli.CommandLine @@ -11,13 +11,13 @@ import org.apache.commons.cli.HelpFormatter import org.apache.commons.cli.Options import org.apache.commons.cli.ParseException -import java.io.BufferedReader import java.io.IOException -import java.util.stream.Collectors +import java.io.InputStream import kotlin.system.exitProcess -import kotlinx.serialization.decodeFromString +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream /** * Class that gives access to properties of a test @@ -29,8 +29,8 @@ import kotlinx.serialization.json.Json class TestArgumentsReader( private val args: Array, val properties: TestFrameworkProperties, - override val classLoader: ClassLoader -) : JsonResourceConfigReader?>() { + classLoader: ClassLoader +) : AbstractResourceConfigReader>(classLoader) { private val cliArguments: List? = readResource(properties.testFrameworkArgsRelativePath) private val cmd: CommandLine by lazy { parseArguments() } @@ -82,14 +82,12 @@ class TestArgumentsReader( /** * Parse JSON to a list of [CliArgument]s * - * @param fileStream a [BufferedReader] representing input JSON + * @param inputStream a [InputStream] representing input JSON * @return list of [CliArgument]s */ + @OptIn(ExperimentalSerializationApi::class) @Throws(IOException::class) - override fun parseResource(fileStream: BufferedReader): List { - val jsonValue = fileStream.lines().collect(Collectors.joining()) - return Json.decodeFromString(jsonValue) - } + override fun parse(inputStream: InputStream): List = Json.decodeFromStream(inputStream) companion object { private val log = KotlinLogging.logger {} diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt index 5322de1932..8e01ed8675 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt @@ -1,33 +1,29 @@ package org.cqfn.diktat.test.framework.config -import org.cqfn.diktat.common.config.reader.JsonResourceConfigReader +import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader -import java.io.BufferedReader import java.io.IOException -import java.util.stream.Collectors +import java.io.InputStream import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream /** - * A [JsonResourceConfigReader] to read tests configuration as [TestConfig] - * @property classLoader a [ClassLoader] to load configutation file + * A [AbstractResourceConfigReader] to read tests configuration as [TestConfig] + * @property classLoader a [ClassLoader] to load configuration file */ -class TestConfigReader(configFilePath: String, override val classLoader: ClassLoader) : JsonResourceConfigReader() { +class TestConfigReader(configFilePath: String, classLoader: ClassLoader) : AbstractResourceConfigReader(classLoader) { /** * The [TestConfig] which is read from */ val config: TestConfig? = readResource(configFilePath) /** - * @param fileStream input stream of data from config file + * @param inputStream input stream of data from config file * @return [TestConfig] read from file */ @OptIn(ExperimentalSerializationApi::class) @Throws(IOException::class) - override fun parseResource(fileStream: BufferedReader): TestConfig { - val jsonValue: String = fileStream.lines().collect(Collectors.joining()) - return Json.decodeFromString(jsonValue) - } + override fun parse(inputStream: InputStream): TestConfig = Json.decodeFromStream(inputStream) } From fe8da75b85aba2f4627bb33f34dadaf00923d5c9 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 13:40:53 +0300 Subject: [PATCH 03/19] introduced DiktatRuleConfigReader --- .../org/cqfn/diktat/DiktatRunnerArguments.kt | 8 +- .../org/cqfn/diktat/DiktatRunnerFactory.kt | 4 +- .../cqfn/diktat/api/DiktatRuleSetFactory.kt | 15 - .../org/cqfn/diktat/cli/DiktatProperties.kt | 2 +- .../diktat/plugin/maven/DiktatBaseMojo.kt | 2 +- .../cqfn/diktat/plugin/maven/DiktatMojo.kt | 5 +- .../rules/DiktatRuleConfigReaderImpl.kt | 21 +- .../ruleset/rules/DiktatRuleSetFactoryImpl.kt | 2 - .../ruleset/rules/DiktatRuleSetProvider.kt | 279 ------------------ .../smoke/RulesConfigValidationTest.kt | 9 +- ...est.kt => DiktatRuleSetFactoryImplTest.kt} | 10 +- .../org/cqfn/diktat/util/FixTestBase.kt | 2 +- .../org/cqfn/diktat/util/LintTestBase.kt | 2 +- .../rules/DiktatRuleSetProviderV3Spi.kt | 6 +- .../diktat/ruleset/smoke/DiktatSmokeTest.kt | 9 +- 15 files changed, 53 insertions(+), 323 deletions(-) delete mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt rename diktat-rules/src/test/kotlin/org/cqfn/diktat/util/{DiktatRuleSetProviderTest.kt => DiktatRuleSetFactoryImplTest.kt} (90%) diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt index 026597824e..3fd0453e9c 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt @@ -1,14 +1,16 @@ package org.cqfn.diktat import org.cqfn.diktat.api.DiktatProcessorListener +import java.io.InputStream import java.io.OutputStream import java.nio.file.Path import kotlin.io.path.absolutePathString +import kotlin.io.path.inputStream /** * Arguments for [DiktatRunner] * - * @property configFileName a config file to load Diktat's rules + * @property configInputStream an input stream with config to load Diktat's rules * @property sourceRootDir a common root dir for all provided [files] * @property files a collection of files which needs to be fixed * @property baselineFile an optional path to file with baseline @@ -19,7 +21,7 @@ import kotlin.io.path.absolutePathString * @property loggingListener listener to log diktat runner phases, [DiktatProcessorListener.empty] by default */ data class DiktatRunnerArguments( - val configFileName: String, + val configInputStream: InputStream, val sourceRootDir: Path, val files: Collection, val baselineFile: Path?, @@ -40,7 +42,7 @@ data class DiktatRunnerArguments( colorNameInPlain: String? = null, loggingListener: DiktatProcessorListener = DiktatProcessorListener.empty, ) : this( - configFile.absolutePathString(), + configFile.inputStream(), sourceRootDir, files, baselineFile, diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt index 726c559529..e04f51de4e 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerFactory.kt @@ -28,8 +28,8 @@ class DiktatRunnerFactory( * @return an instance of [DiktatRunner] created using [args] */ override fun invoke(args: DiktatRunnerArguments): DiktatRunner { - val diktatRuleConfigs = diktatRuleConfigReader(args.configFileName) - val diktatRuleSet = diktatRuleSetFactory create(args.configFileName) + val diktatRuleConfigs = diktatRuleConfigReader(args.configInputStream) + val diktatRuleSet = diktatRuleSetFactory(diktatRuleConfigs) val processor = diktatProcessorFactory(diktatRuleSet) val (baseline, baselineGenerator) = resolveBaseline(args.baselineFile, args.sourceRootDir) val (reporter, closer) = resolveReporter( diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt index 674f934175..255d99ee8d 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt @@ -1,8 +1,5 @@ package org.cqfn.diktat.api -import java.nio.file.Path -import kotlin.io.path.absolutePathString - /** * A factory which creates a [DiktatRuleSet]. */ @@ -12,16 +9,4 @@ interface DiktatRuleSetFactory : Function1, DiktatRuleSet * @return the default instance of [DiktatRuleSet] */ override operator fun invoke(rulesConfig: List): DiktatRuleSet - - /** - * @param configFile a path to file with configuration for diktat (`diktat-analysis.yml`) - * @return created [DiktatRuleSet] using [configFile] - */ - fun create(configFile: String): DiktatRuleSet - - /** - * @param configFile a file with configuration for diktat (`diktat-analysis.yml`) - * @return created [DiktatRuleSet] using [configFile] - */ - fun create(configFile: Path): DiktatRuleSet = create(configFile.absolutePathString()) } diff --git a/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt b/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt index 313a35a8b8..70c40b2d88 100644 --- a/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt +++ b/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt @@ -69,7 +69,7 @@ data class DiktatProperties( sourceRootDir: Path, loggingListener: DiktatProcessorListener, ): DiktatRunnerArguments = DiktatRunnerArguments( - configFileName = config, + configInputStream = config, sourceRootDir = sourceRootDir, files = getFiles(sourceRootDir), baselineFile = null, 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 f7c125603d..d61d719285 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 @@ -109,7 +109,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { diktatReporterFactory = DiktatReporterFactoryImpl() ) val args = DiktatRunnerArguments( - configFileName = resolveConfig(), + configInputStream = resolveConfig(), sourceRootDir = sourceRootDir, files = inputs.map(::Path), baselineFile = baseline?.toPath(), 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 57472711b2..885dc12512 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 @@ -6,12 +6,11 @@ package org.cqfn.diktat.plugin.maven import org.cqfn.diktat.DiktatRunner import org.cqfn.diktat.DiktatRunnerArguments -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.apache.maven.plugins.annotations.Mojo /** - * Main [Mojo] that call [DiktatRuleSetProvider]'s rules on [inputs] files + * Main [Mojo] that call diktat's rules on [inputs] files */ @Mojo(name = "check") @Suppress("unused") @@ -23,7 +22,7 @@ class DiktatCheckMojo : DiktatBaseMojo() { } /** - * Main [Mojo] that call [DiktatRuleSetProvider]'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/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index 38c01cb376..de0b52c70f 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -2,6 +2,7 @@ package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.api.DiktatRuleConfig import org.cqfn.diktat.api.DiktatRuleConfigReader +import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON import org.cqfn.diktat.common.config.rules.DIKTAT_CONF_PROPERTY @@ -32,13 +33,31 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { companion object { private val log = KotlinLogging.logger {} + private val classLoader: ClassLoader = object {}.javaClass.classLoader /** * @param diktatConfigFile the configuration file where all configurations for * inspections and rules are stored. * @return resolved existed diktatConfigFile */ - fun resolveConfigFile(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): String { + fun readConfigFile(diktatConfigFile: String = DIKTAT_CONF_PROPERTY): InputStream { + val resourceFileName = resolveConfigFile(diktatConfigFile) + val resourceFile = File(resourceFileName) + return if (resourceFile.exists()) { + log.debug { "Using $DIKTAT_ANALYSIS_CONF file from the following path: ${resourceFile.absolutePath}" } + resourceFile.inputStream() + } else { + log.debug { "Using the default $DIKTAT_ANALYSIS_CONF file from the class path" } + classLoader.getResourceAsStream(resourceFileName) ?: run { + log.error { "Not able to open file $resourceFileName from the resources" } + object : InputStream() { + override fun read(): Int = -1 + } + } + } + } + + private fun resolveConfigFile(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): String { val possibleConfigs: Sequence = sequence { yield(resolveDefaultConfig(diktatConfigFile)) yield(resolveConfigFileFromJarLocation(diktatConfigFile)) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt index e06110f721..eb5b227813 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetFactoryImpl.kt @@ -200,6 +200,4 @@ class DiktatRuleSetFactoryImpl : DiktatRuleSetFactory { .toList() return DiktatRuleSet(rules) } - - override fun create(configFile: String): DiktatRuleSet = DiktatRuleSetProvider(configFile).invoke() } 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 deleted file mode 100644 index de7dac4a29..0000000000 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt +++ /dev/null @@ -1,279 +0,0 @@ -package org.cqfn.diktat.ruleset.rules - -import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF -import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON -import org.cqfn.diktat.common.config.rules.DIKTAT_CONF_PROPERTY -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.constants.Warnings -import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming -import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming -import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming -import org.cqfn.diktat.ruleset.rules.chapter2.comments.CommentsRule -import org.cqfn.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.CommentsFormatting -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocComments -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocFormatting -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocMethods -import org.cqfn.diktat.ruleset.rules.chapter3.AnnotationNewLineRule -import org.cqfn.diktat.ruleset.rules.chapter3.BlockStructureBraces -import org.cqfn.diktat.ruleset.rules.chapter3.BooleanExpressionsRule -import org.cqfn.diktat.ruleset.rules.chapter3.BracesInConditionalsAndLoopsRule -import org.cqfn.diktat.ruleset.rules.chapter3.ClassLikeStructuresOrderRule -import org.cqfn.diktat.ruleset.rules.chapter3.CollapseIfStatementsRule -import org.cqfn.diktat.ruleset.rules.chapter3.ConsecutiveSpacesRule -import org.cqfn.diktat.ruleset.rules.chapter3.DebugPrintRule -import org.cqfn.diktat.ruleset.rules.chapter3.EmptyBlock -import org.cqfn.diktat.ruleset.rules.chapter3.EnumsSeparated -import org.cqfn.diktat.ruleset.rules.chapter3.LineLength -import org.cqfn.diktat.ruleset.rules.chapter3.LongNumericalValuesSeparatedRule -import org.cqfn.diktat.ruleset.rules.chapter3.MagicNumberRule -import org.cqfn.diktat.ruleset.rules.chapter3.MultipleModifiersSequence -import org.cqfn.diktat.ruleset.rules.chapter3.NullableTypeRule -import org.cqfn.diktat.ruleset.rules.chapter3.RangeConventionalRule -import org.cqfn.diktat.ruleset.rules.chapter3.SingleLineStatementsRule -import org.cqfn.diktat.ruleset.rules.chapter3.SortRule -import org.cqfn.diktat.ruleset.rules.chapter3.StringConcatenationRule -import org.cqfn.diktat.ruleset.rules.chapter3.StringTemplateFormatRule -import org.cqfn.diktat.ruleset.rules.chapter3.TrailingCommaRule -import org.cqfn.diktat.ruleset.rules.chapter3.WhenMustHaveElseRule -import org.cqfn.diktat.ruleset.rules.chapter3.files.BlankLinesRule -import org.cqfn.diktat.ruleset.rules.chapter3.files.FileSize -import org.cqfn.diktat.ruleset.rules.chapter3.files.FileStructureRule -import org.cqfn.diktat.ruleset.rules.chapter3.files.IndentationRule -import org.cqfn.diktat.ruleset.rules.chapter3.files.NewlinesRule -import org.cqfn.diktat.ruleset.rules.chapter3.files.TopLevelOrderRule -import org.cqfn.diktat.ruleset.rules.chapter3.files.WhiteSpaceRule -import org.cqfn.diktat.ruleset.rules.chapter3.identifiers.LocalVariablesRule -import org.cqfn.diktat.ruleset.rules.chapter4.ImmutableValNoVarRule -import org.cqfn.diktat.ruleset.rules.chapter4.NullChecksRule -import org.cqfn.diktat.ruleset.rules.chapter4.SmartCastRule -import org.cqfn.diktat.ruleset.rules.chapter4.TypeAliasRule -import org.cqfn.diktat.ruleset.rules.chapter4.VariableGenericTypeDeclarationRule -import org.cqfn.diktat.ruleset.rules.chapter4.calculations.AccurateCalculationsRule -import org.cqfn.diktat.ruleset.rules.chapter5.AsyncAndSyncRule -import org.cqfn.diktat.ruleset.rules.chapter5.AvoidNestedFunctionsRule -import org.cqfn.diktat.ruleset.rules.chapter5.CheckInverseMethodRule -import org.cqfn.diktat.ruleset.rules.chapter5.CustomLabel -import org.cqfn.diktat.ruleset.rules.chapter5.FunctionArgumentsSize -import org.cqfn.diktat.ruleset.rules.chapter5.FunctionLength -import org.cqfn.diktat.ruleset.rules.chapter5.LambdaLengthRule -import org.cqfn.diktat.ruleset.rules.chapter5.LambdaParameterOrder -import org.cqfn.diktat.ruleset.rules.chapter5.NestedFunctionBlock -import org.cqfn.diktat.ruleset.rules.chapter5.OverloadingArgumentsFunction -import org.cqfn.diktat.ruleset.rules.chapter5.ParameterNameInOuterLambdaRule -import org.cqfn.diktat.ruleset.rules.chapter6.AvoidEmptyPrimaryConstructor -import org.cqfn.diktat.ruleset.rules.chapter6.AvoidUtilityClass -import org.cqfn.diktat.ruleset.rules.chapter6.CustomGetterSetterRule -import org.cqfn.diktat.ruleset.rules.chapter6.ExtensionFunctionsInFileRule -import org.cqfn.diktat.ruleset.rules.chapter6.ExtensionFunctionsSameNameRule -import org.cqfn.diktat.ruleset.rules.chapter6.ImplicitBackingPropertyRule -import org.cqfn.diktat.ruleset.rules.chapter6.PropertyAccessorFields -import org.cqfn.diktat.ruleset.rules.chapter6.RunInScript -import org.cqfn.diktat.ruleset.rules.chapter6.TrivialPropertyAccessors -import org.cqfn.diktat.ruleset.rules.chapter6.UseLastIndex -import org.cqfn.diktat.ruleset.rules.chapter6.UselessSupertype -import org.cqfn.diktat.ruleset.rules.chapter6.classes.AbstractClassesRule -import org.cqfn.diktat.ruleset.rules.chapter6.classes.CompactInitialization -import org.cqfn.diktat.ruleset.rules.chapter6.classes.DataClassesRule -import org.cqfn.diktat.ruleset.rules.chapter6.classes.InlineClassesRule -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 mu.KotlinLogging -import org.jetbrains.kotlin.org.jline.utils.Levenshtein - -import java.io.File - -/** - * _KtLint_-agnostic factory which creates a [DiktatRuleSet]. - * - * 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. - * - * @param diktatConfigFile the configuration file where all configurations for - * inspections and rules are stored. - */ -class DiktatRuleSetProvider(private val diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) { - private val possibleConfigs: Sequence = sequence { - yield(resolveDefaultConfig()) - yield(resolveConfigFileFromJarLocation()) - yield(resolveConfigFileFromSystemProperty()) - } - private val configRules: List by lazy { - log.debug("Will run $DIKTAT_RULE_SET_ID with $diktatConfigFile" + - " (it can be placed to the run directory or the default file from resources will be used)") - val configPath = possibleConfigs - .firstOrNull { it != null && File(it).exists() } - val resultedDiktatConfigFile = configPath - ?: run { - val possibleConfigsList = possibleConfigs.toList() - log.warn( - "Configuration file not found in directory where diktat is run (${possibleConfigsList[0]}) " + - "or in the directory where diktat.jar is stored (${possibleConfigsList[1]}) " + - "or in system property (${possibleConfigsList[2]}), " + - "the default file included in jar will be used. " + - "Some configuration options will be disabled or substituted with defaults. " + - "Custom configuration file should be placed in diktat working directory if run from CLI " + - "or provided as configuration options in plugins." - ) - diktatConfigFile - } - - RulesConfigReader(javaClass.classLoader) - .readResource(resultedDiktatConfigFile) - ?.onEach(::validate) - ?: emptyList() - } - - /** - * 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 [com.pinterest.ktlint.core.KtLintRuleEngine.lint] and [com.pinterest.ktlint.core.KtLintRuleEngine.format] the [DiktatRuleSet] - * is retrieved. - * This results in new instances of each [com.pinterest.ktlint.core.Rule] for each file being - * processed. - * As of that a [com.pinterest.ktlint.core.Rule] does not need to be thread-safe. - * - * However, [com.pinterest.ktlint.core.KtLintRuleEngine.format] requires the [com.pinterest.ktlint.core.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. - * - * @return a default [DiktatRuleSet] - */ - @Suppress( - "LongMethod", - "TOO_LONG_FUNCTION", - ) - operator 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 = sequenceOf( - // comments & documentation - ::CommentsRule, - ::SingleConstructorRule, // this rule can add properties to a primary constructor, so should be before KdocComments - ::KdocComments, - ::KdocMethods, - ::KdocFormatting, - ::CommentsFormatting, - // naming - ::FileNaming, - ::PackageNaming, - ::IdentifierNaming, - // code structure - ::UselessSupertype, - ::ClassLikeStructuresOrderRule, - ::WhenMustHaveElseRule, - ::BracesInConditionalsAndLoopsRule, - ::EmptyBlock, - ::AvoidEmptyPrimaryConstructor, - ::TopLevelOrderRule, - ::SingleLineStatementsRule, - ::MultipleModifiersSequence, - ::TrivialPropertyAccessors, - ::CustomGetterSetterRule, - ::CompactInitialization, - // other rules - ::UseLastIndex, - ::InlineClassesRule, - ::ExtensionFunctionsInFileRule, - ::CheckInverseMethodRule, - ::StatelessClassesRule, - ::ImplicitBackingPropertyRule, - ::DataClassesRule, - ::LocalVariablesRule, - ::SmartCastRule, - ::AvoidUtilityClass, - ::PropertyAccessorFields, - ::AbstractClassesRule, - ::TrailingCommaRule, - ::SingleInitRule, - ::RangeConventionalRule, - ::DebugPrintRule, - ::CustomLabel, - ::VariableGenericTypeDeclarationRule, - ::LongNumericalValuesSeparatedRule, - ::NestedFunctionBlock, - ::AnnotationNewLineRule, - ::SortRule, - ::EnumsSeparated, - ::StringConcatenationRule, - ::StringTemplateFormatRule, - ::AccurateCalculationsRule, - ::CollapseIfStatementsRule, - ::LineLength, - ::RunInScript, - ::TypeAliasRule, - ::OverloadingArgumentsFunction, - ::FunctionLength, - ::MagicNumberRule, - ::LambdaParameterOrder, - ::FunctionArgumentsSize, - ::BlankLinesRule, - ::FileSize, - ::AsyncAndSyncRule, - ::NullableTypeRule, - ::NullChecksRule, - ::ImmutableValNoVarRule, - ::AvoidNestedFunctionsRule, - ::ExtensionFunctionsSameNameRule, - ::LambdaLengthRule, - ::BooleanExpressionsRule, - ::ParameterNameInOuterLambdaRule, - // formatting: moving blocks, adding line breaks, indentations etc. - ::BlockStructureBraces, - ::ConsecutiveSpacesRule, - ::HeaderCommentRule, - ::FileStructureRule, // this rule should be right before indentation because it should operate on already valid code - ::NewlinesRule, // newlines need to be inserted right before fixing indentation - ::WhiteSpaceRule, // this rule should be after other rules that can cause wrong spacing - ::IndentationRule, // indentation rule should be the last because it fixes formatting after all the changes done by previous rules - - ) - .map { - it(configRules) - } - .toList() - return DiktatRuleSet(rules) - } - - private fun validate(config: RulesConfig) = - require(config.name == DIKTAT_COMMON || config.name in Warnings.names) { - val closestMatch = Warnings.names.minByOrNull { Levenshtein.distance(it, config.name) } - "Warning name <${config.name}> in configuration file is invalid, did you mean <$closestMatch>?" - } - - private fun resolveDefaultConfig() = diktatConfigFile - - private fun resolveConfigFileFromJarLocation(): String { - // 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 = - javaClass - .protectionDomain - .codeSource - .location - .toURI() - - val configPathWithFileName = File(ruleSetProviderPath).absolutePath - - val indexOfName = configPathWithFileName.lastIndexOf(File.separator) - val configPath = if (indexOfName > -1) configPathWithFileName.substring(0, indexOfName) else configPathWithFileName - - return "$configPath${File.separator}$diktatConfigFile" - } - - private fun resolveConfigFileFromSystemProperty(): String? = System.getProperty(DIKTAT_CONF_PROPERTY) - - companion object { - private val log = KotlinLogging.logger {} - } -} 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 decaa93a00..346efb53aa 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,7 @@ package org.cqfn.diktat.ruleset.smoke -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently import com.charleskorn.kaml.InvalidPropertyValueException @@ -39,7 +40,7 @@ class RulesConfigValidationTest { """.trimMargin() ) val exception = assertThrows { - DiktatRuleSetProvider(file.absolutePath).invoke() + DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(file.inputStream())) } Assertions.assertEquals("Warning name in configuration file is invalid, did you mean ?", exception.message) } @@ -54,7 +55,7 @@ class RulesConfigValidationTest { """.trimMargin() ) assertThrows { - DiktatRuleSetProvider(file.absolutePath).invoke() + DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(file.inputStream())) } } @@ -70,6 +71,6 @@ class RulesConfigValidationTest { | isIncludeHeader: Fslse """.trimMargin() ) - DiktatRuleSetProvider(file.absolutePath).invoke() + DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(file.inputStream())) } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt similarity index 90% rename from diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt rename to diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt index 2ea069c128..fbadd9f3f6 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetProviderTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt @@ -8,7 +8,7 @@ import org.cqfn.diktat.api.DiktatRuleSet import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.RulesConfigReader import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import org.cqfn.diktat.test.framework.util.filterContentMatches import org.assertj.core.api.Assertions.assertThat @@ -21,11 +21,11 @@ import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension import kotlin.io.path.walk -class DiktatRuleSetProviderTest { +class DiktatRuleSetFactoryImplTest { @OptIn(ExperimentalPathApi::class) @Suppress("UnsafeCallOnNullableType") @Test - fun `check DiktatRuleSetProviderTest contain all rules`() { + fun `check DiktatRuleSetFactoryImpl contain all rules`() { val path = "${System.getProperty("user.dir")}/src/main/kotlin/org/cqfn/diktat/ruleset/rules" val fileNames = Path(path) .walk() @@ -34,8 +34,8 @@ class DiktatRuleSetProviderTest { .map(Path::nameWithoutExtension) .filterNot { it in ignoredFileNames } .toList() - val ruleNames = DiktatRuleSetProvider() - .invoke() + val ruleNames = DiktatRuleSetFactoryImpl() + .invoke(emptyList()) .rules .asSequence() .map { it::class.simpleName } 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 177e5d6095..852222f96e 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,7 +7,7 @@ import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.test.framework.processing.FileComparisonResult import org.cqfn.diktat.test.framework.processing.ResourceReader import org.cqfn.diktat.test.framework.processing.TestComparatorUnit -import org.cqfn.diktat.util.DiktatRuleSetProviderTest.Companion.diktatRuleSetForTest +import org.cqfn.diktat.util.DiktatRuleSetFactoryImplTest.Companion.diktatRuleSetForTest import mu.KotlinLogging import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Assertions 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 4cf05697e6..fe54c63572 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,7 +4,7 @@ import org.cqfn.diktat.api.DiktatCallback import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.ktlint.lint import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.util.DiktatRuleSetProviderTest.Companion.diktatRuleSetForTest +import org.cqfn.diktat.util.DiktatRuleSetFactoryImplTest.Companion.diktatRuleSetForTest import org.cqfn.diktat.api.DiktatError import org.assertj.core.api.Assertions.assertThat import org.intellij.lang.annotations.Language diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt index 72e9ceace3..caeea52509 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt @@ -27,5 +27,9 @@ class DiktatRuleSetProviderV3Spi : RuleSetProviderV3( KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).initKtLintKLogger() } - override fun getRuleProviders(): Set = DiktatRuleSetProvider().invoke().toKtLint() + private val diktatRuleConfigReader = DiktatRuleConfigReaderImpl() + private val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() + + override fun getRuleProviders(): Set = diktatRuleSetFactory(diktatRuleConfigReader(DiktatRuleConfigReaderImpl.readConfigFile())) + .toKtLint() } 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 4924dea0bc..629780bd9a 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,15 +2,16 @@ package org.cqfn.diktat.ruleset.smoke import org.cqfn.diktat.api.DiktatError import org.cqfn.diktat.ktlint.format -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import org.cqfn.diktat.test.framework.processing.TestComparatorUnit import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import java.nio.file.Path -import kotlin.io.path.absolutePathString +import kotlin.io.path.inputStream /** - * Test for [DiktatRuleSetProvider] in autocorrect mode as a whole. All rules are applied to a file. + * Test for [DiktatRuleSetFactoryImpl] 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. */ @@ -42,7 +43,7 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { resourceFilePath = RESOURCE_FILE_PATH, function = { testFile -> format( - ruleSetSupplier = { DiktatRuleSetProvider(config.absolutePathString()).invoke() }, + ruleSetSupplier = { DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(config.inputStream())) }, file = testFile, cb = { lintError, _ -> unfixedLintErrors.add(lintError) }, ) From 4d7f21105886c40932641bacd27fb0ea9a680b13 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 13:42:28 +0300 Subject: [PATCH 04/19] removed DiktatMainRunner --- .../org/cqfn/diktat/DiktatMainRunner.kt | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt diff --git a/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt b/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt deleted file mode 100644 index 5559ee6f76..0000000000 --- a/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMainRunner.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.cqfn.diktat - -import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.api.DiktatRuleSetFactory -import org.cqfn.diktat.ktlint.DiktatProcessorFactoryImpl -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl -import mu.KotlinLogging -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.core.LoggerContext -import org.slf4j.event.Level - -private val log = KotlinLogging.logger { } - -fun main() { - // a temporary - val logLevel = Level.ERROR - // 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() - - // default implementations - val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() - val diktatProcessorFactory = DiktatProcessorFactoryImpl() - - // ruleSet - val diktatRuleSet: DiktatRuleSet = diktatRuleSetFactory() - val diktatProcessor: DiktatProcessor = diktatProcessorFactory(diktatRuleSet) - - val code = mutableListOf() - while (true) { - val line = readln() - if (line == "CHECK") { - diktatProcessor.check( - code = code.joinToString(), - isScript = true - ) { error, _ -> - println(error.toString()) - } - code.clear() - } else if (line == "FIX") { - val result = diktatProcessor.fix( - code = code.joinToString(), - isScript = true - ) { error, _ -> - println(error.toString()) - } - code.clear() - println("Formatted code:") - println(result) - } else if (line == "END") { - break - } else { - code.add(line) - } - } -} From 72435e78b9c029c5122c0c8b9f2b32003ab7a956 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 14:07:25 +0300 Subject: [PATCH 05/19] fixed test failures --- diktat-api/build.gradle.kts | 2 ++ .../org/cqfn/diktat/api/DiktatRuleConfig.kt | 3 +++ .../main/kotlin/org/cqfn/diktat/DiktatMain.kt | 2 ++ .../org/cqfn/diktat/cli/DiktatProperties.kt | 3 ++- .../reader/AbstractResourceConfigReader.kt | 2 +- .../diktat/plugin/gradle/tasks/DiktatTaskBase.kt | 2 ++ .../cqfn/diktat/plugin/maven/DiktatBaseMojo.kt | 16 +++++++++------- .../ruleset/rules/DiktatRuleConfigReaderImpl.kt | 2 +- .../paragraph3/newlines/SemicolonsExpected.kt | 2 ++ .../test/paragraph3/newlines/SemicolonsTest.kt | 2 ++ 10 files changed, 26 insertions(+), 10 deletions(-) diff --git a/diktat-api/build.gradle.kts b/diktat-api/build.gradle.kts index d27314538f..44b6501d2c 100644 --- a/diktat-api/build.gradle.kts +++ b/diktat-api/build.gradle.kts @@ -2,12 +2,14 @@ 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") + alias(libs.plugins.kotlin.plugin.serialization) } project.description = "This module builds diktat-api" dependencies { implementation(libs.kotlin.compiler.embeddable) + implementation(libs.kotlinx.serialization.core) } val generateDiktatVersionFile by tasks.registering { diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt index 3c93cd675d..39458e7c18 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfig.kt @@ -1,5 +1,7 @@ package org.cqfn.diktat.api +import kotlinx.serialization.Serializable + /** * Configuration of individual [DiktatRule] * @@ -8,6 +10,7 @@ package org.cqfn.diktat.api * @property configuration a map of strings with configuration options * @property ignoreAnnotated if a code block is marked with these annotations - it will not be checked by this rule */ +@Serializable data class DiktatRuleConfig( val name: String, val enabled: Boolean = true, diff --git a/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt b/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt index aae16f9407..b1ecd162b1 100644 --- a/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt +++ b/diktat-cli/src/main/kotlin/org/cqfn/diktat/DiktatMain.kt @@ -10,6 +10,7 @@ import org.cqfn.diktat.cli.DiktatProperties import org.cqfn.diktat.ktlint.DiktatBaselineFactoryImpl import org.cqfn.diktat.ktlint.DiktatProcessorFactoryImpl import org.cqfn.diktat.ktlint.DiktatReporterFactoryImpl +import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import mu.KotlinLogging @@ -31,6 +32,7 @@ private val loggingListener = object : DiktatProcessorListener { fun main(args: Array) { val diktatRunnerFactory = DiktatRunnerFactory( + DiktatRuleConfigReaderImpl(), DiktatRuleSetFactoryImpl(), DiktatProcessorFactoryImpl(), DiktatBaselineFactoryImpl(), diff --git a/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt b/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt index 70c40b2d88..00ce132405 100644 --- a/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt +++ b/diktat-cli/src/main/kotlin/org/cqfn/diktat/cli/DiktatProperties.kt @@ -17,6 +17,7 @@ import java.io.OutputStream import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.createDirectories +import kotlin.io.path.inputStream import kotlin.io.path.outputStream import kotlin.system.exitProcess import kotlinx.cli.ArgParser @@ -69,7 +70,7 @@ data class DiktatProperties( sourceRootDir: Path, loggingListener: DiktatProcessorListener, ): DiktatRunnerArguments = DiktatRunnerArguments( - configInputStream = config, + configInputStream = Paths.get(config).inputStream(), sourceRootDir = sourceRootDir, files = getFiles(sourceRootDir), baselineFile = null, diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt index a18200bc89..e9d665b45d 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt @@ -13,7 +13,7 @@ import java.io.InputStream * b. parse - implement parser for your file format (for example parse it to a proper json) * 2) Use your new class MyReader(javaClass.classLoader).readResource("some/path/to/file.format") * - * @param classLoader The [ClassLoader] used to load the requested resource. + * @property classLoader The [ClassLoader] used to load the requested resource. * @param T - class name parameter that will be used in calculation of classpath */ abstract class AbstractResourceConfigReader( 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 acba224986..24aadf1370 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 @@ -10,6 +10,7 @@ import org.cqfn.diktat.ktlint.DiktatReporterFactoryImpl import org.cqfn.diktat.plugin.gradle.DiktatExtension import org.cqfn.diktat.plugin.gradle.getOutputFile import org.cqfn.diktat.plugin.gradle.getReporterType +import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import generated.DIKTAT_VERSION @@ -66,6 +67,7 @@ abstract class DiktatTaskBase( } private val diktatRunnerFactory by lazy { DiktatRunnerFactory( + diktatRuleConfigReader = DiktatRuleConfigReaderImpl(), diktatRuleSetFactory = DiktatRuleSetFactoryImpl(), diktatProcessorFactory = DiktatProcessorFactoryImpl(), diktatBaselineFactory = DiktatBaselineFactoryImpl(), 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 d61d719285..ef9e145f83 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,6 +6,7 @@ import org.cqfn.diktat.DiktatRunnerFactory import org.cqfn.diktat.ktlint.DiktatBaselineFactoryImpl import org.cqfn.diktat.ktlint.DiktatProcessorFactoryImpl import org.cqfn.diktat.ktlint.DiktatReporterFactoryImpl +import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import org.apache.maven.plugin.AbstractMojo @@ -94,7 +95,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { */ override fun execute() { val configFile = resolveConfig() - if (!File(configFile).exists()) { + if (!configFile.exists()) { throw MojoExecutionException("Configuration file $diktatConfigFile doesn't exist") } log.info("Running diKTat plugin with configuration file $configFile and inputs $inputs" + @@ -103,13 +104,14 @@ abstract class DiktatBaseMojo : AbstractMojo() { val sourceRootDir = mavenProject.basedir.parentFile.toPath() val diktatRunnerFactory = DiktatRunnerFactory( + diktatRuleConfigReader = DiktatRuleConfigReaderImpl(), diktatRuleSetFactory = DiktatRuleSetFactoryImpl(), diktatProcessorFactory = DiktatProcessorFactoryImpl(), diktatBaselineFactory = DiktatBaselineFactoryImpl(), diktatReporterFactory = DiktatReporterFactoryImpl() ) val args = DiktatRunnerArguments( - configInputStream = resolveConfig(), + configInputStream = configFile.inputStream(), sourceRootDir = sourceRootDir, files = inputs.map(::Path), baselineFile = baseline?.toPath(), @@ -148,11 +150,12 @@ abstract class DiktatBaseMojo : AbstractMojo() { * If [diktatConfigFile] is absolute, it's path is used. If [diktatConfigFile] is relative, this method looks for it in all maven parent projects. * This way config file can be placed in parent module directory and used in all child modules too. * - * @return path to configuration file as a string. File by this path might not exist. + * @return a configuration file. File by this path might not exist. */ - private fun resolveConfig(): String { - if (File(diktatConfigFile).isAbsolute) { - return diktatConfigFile + private fun resolveConfig(): File { + val file = File(diktatConfigFile) + if (file.isAbsolute) { + return file } return generateSequence(mavenProject) { it.parent } @@ -160,6 +163,5 @@ abstract class DiktatBaseMojo : AbstractMojo() { .run { firstOrNull { it.exists() } ?: first() } - .absolutePath } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index de0b52c70f..e4aee60d47 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -90,7 +90,7 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { // 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 = - javaClass + object {}.javaClass .protectionDomain .codeSource .location diff --git a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt index 71e83bb27f..c0bf00a2b4 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt @@ -1,5 +1,7 @@ package test.paragraph3.newlines +import org.cqfn.diktat.common.config.rules.RulesConfig; + enum class Example { A, B diff --git a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt index 6a8385f9e0..4e19dead42 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt @@ -1,5 +1,7 @@ package test.paragraph3.newlines; +import org.cqfn.diktat.common.config.rules.RulesConfig; + enum class Example { A, B From d9acdcea688f7c9de00c7e1fc852e71e011b5df7 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 14:09:18 +0300 Subject: [PATCH 06/19] fixing a test --- .../resources/test/paragraph3/newlines/SemicolonsExpected.kt | 4 ++-- .../test/resources/test/paragraph3/newlines/SemicolonsTest.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt index c0bf00a2b4..669e47dc26 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsExpected.kt @@ -1,6 +1,6 @@ package test.paragraph3.newlines -import org.cqfn.diktat.common.config.rules.RulesConfig; +import org.cqfn.diktat.common.config.rules.RulesConfig enum class Example { A, @@ -10,4 +10,4 @@ enum class Example { fun foo() {} val a = 0 val b = if (condition) { bar(); baz()} else qux -} +} \ No newline at end of file diff --git a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt index 4e19dead42..f16c4542c4 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/newlines/SemicolonsTest.kt @@ -10,4 +10,4 @@ enum class Example { fun foo() {}; val a = 0; val b = if (condition) { bar(); baz()} else qux -}; +}; \ No newline at end of file From eba7945df571f8cca377830b07bbf9cd4c371996 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 14:45:54 +0300 Subject: [PATCH 07/19] fixed test failures --- .../diktat/common/config/rules/RulesConfigReader.kt | 4 +--- .../diktat/plugin/gradle/DiktatJavaExecTaskTest.kt | 10 ++++++++++ .../ruleset/rules/DiktatRuleConfigReaderImpl.kt | 11 ++++------- .../ruleset/rules/DiktatRuleSetProviderV3Spi.kt | 6 +++--- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt index e903bbd38c..59515c966c 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt @@ -19,7 +19,7 @@ import java.io.InputStream import java.util.Locale import java.util.concurrent.atomic.AtomicInteger -import kotlinx.serialization.Serializable +typealias RulesConfig = DiktatRuleConfig /** * Name of common configuration @@ -49,8 +49,6 @@ interface Rule { fun ruleName(): String } -typealias RulesConfig = DiktatRuleConfig - /** * Configuration that allows customizing additional options of particular rules. * @property config a map of strings with configuration options for a particular rule 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 d8a3f087e1..d67e969e80 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 @@ -27,6 +27,16 @@ class DiktatJavaExecTaskTest { // mock kotlin sources project.mkdir("src/main/kotlin") project.file("src/main/kotlin/Test.kt").createNewFile() + sequenceOf( + project.file("diktat-analysis.yml"), + project.file("../diktat-analysis.yml") + ).forEach { + it.writeText(""" + # Common configuration + - name: DIKTAT_COMMON + enabled: true + """.trimIndent()) + } } @AfterEach diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index e4aee60d47..3e543c9358 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -2,7 +2,6 @@ package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.api.DiktatRuleConfig import org.cqfn.diktat.api.DiktatRuleConfigReader -import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON import org.cqfn.diktat.common.config.rules.DIKTAT_CONF_PROPERTY @@ -18,12 +17,10 @@ import java.io.InputStream * Default implementation for [DiktatRuleConfigReader] */ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { - override fun invoke(inputStream: InputStream): List { - return RulesConfigReader(javaClass.classLoader) - .read(inputStream) - ?.onEach(::validate) - ?: emptyList() - } + override fun invoke(inputStream: InputStream): List = RulesConfigReader(javaClass.classLoader) + .read(inputStream) + ?.onEach(::validate) + ?: emptyList() private fun validate(config: org.cqfn.diktat.common.config.rules.RulesConfig) = require(config.name == DIKTAT_COMMON || config.name in Warnings.names) { diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt index caeea52509..f41776dc3b 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt @@ -22,14 +22,14 @@ import org.slf4j.Logger class DiktatRuleSetProviderV3Spi : RuleSetProviderV3( id = RuleSetId(DIKTAT_RULE_SET_ID), ) { + private val diktatRuleConfigReader = DiktatRuleConfigReaderImpl() + private val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() + init { // Need to init KtLint logger to set log level from CLI KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).initKtLintKLogger() } - private val diktatRuleConfigReader = DiktatRuleConfigReaderImpl() - private val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() - override fun getRuleProviders(): Set = diktatRuleSetFactory(diktatRuleConfigReader(DiktatRuleConfigReaderImpl.readConfigFile())) .toKtLint() } From 5ae7124b0f8317670303752ddef99dd4a567f108 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 16:00:09 +0300 Subject: [PATCH 08/19] fixed test failures --- .../rules/DiktatRuleConfigReaderImpl.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index 3e543c9358..bf0739f4da 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -30,14 +30,15 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { companion object { private val log = KotlinLogging.logger {} - private val classLoader: ClassLoader = object {}.javaClass.classLoader + @Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") + private val javaClass = object {}.javaClass /** * @param diktatConfigFile the configuration file where all configurations for * inspections and rules are stored. * @return resolved existed diktatConfigFile */ - fun readConfigFile(diktatConfigFile: String = DIKTAT_CONF_PROPERTY): InputStream { + fun readConfigFile(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): InputStream { val resourceFileName = resolveConfigFile(diktatConfigFile) val resourceFile = File(resourceFileName) return if (resourceFile.exists()) { @@ -45,7 +46,7 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { resourceFile.inputStream() } else { log.debug { "Using the default $DIKTAT_ANALYSIS_CONF file from the class path" } - classLoader.getResourceAsStream(resourceFileName) ?: run { + javaClass.classLoader.getResourceAsStream(resourceFileName) ?: run { log.error { "Not able to open file $resourceFileName from the resources" } object : InputStream() { override fun read(): Int = -1 @@ -86,12 +87,11 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { private fun resolveConfigFileFromJarLocation(diktatConfigFile: String): String { // 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 = - object {}.javaClass - .protectionDomain - .codeSource - .location - .toURI() + val ruleSetProviderPath = javaClass + .protectionDomain + .codeSource + .location + .toURI() val configPathWithFileName = File(ruleSetProviderPath).absolutePath From a8cb7fb7860adf96d6371236d7201e3eab9d65d4 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 16:27:57 +0300 Subject: [PATCH 09/19] removed AbstractResourceConfigReader --- diktat-api/build.gradle.kts | 8 +++ .../org/cqfn/diktat/DiktatRunnerArguments.kt | 1 - .../config/reader/AbstractConfigReader.kt | 7 ++- .../reader/AbstractResourceConfigReader.kt | 50 ------------------- .../common/config/rules/RulesConfigReader.kt | 24 +-------- .../org/cqfn/diktat/test/ConfigReaderTest.kt | 8 +-- diktat-dev-ksp/build.gradle.kts | 7 +++ .../rules/DiktatRuleConfigReaderImpl.kt | 4 +- .../ruleset/utils/RulesConfigYamlTest.kt | 4 +- .../util/DiktatRuleSetFactoryImplTest.kt | 4 +- .../ruleset/smoke/DiktatSmokeTestBase.kt | 3 +- .../framework/config/TestArgumentsReader.kt | 8 +-- .../test/framework/config/TestConfigReader.kt | 9 ++-- 13 files changed, 43 insertions(+), 94 deletions(-) delete mode 100644 diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt diff --git a/diktat-api/build.gradle.kts b/diktat-api/build.gradle.kts index 44b6501d2c..f04aabfe18 100644 --- a/diktat-api/build.gradle.kts +++ b/diktat-api/build.gradle.kts @@ -41,3 +41,11 @@ kotlin.sourceSets.getByName("main") { } ) } + +tasks.named("diktatFix") { + dependsOn( + generateDiktatVersionFile, + tasks.named("compileKotlin"), + tasks.named("processResources"), + ) +} diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt index 3fd0453e9c..80d760759b 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatRunnerArguments.kt @@ -4,7 +4,6 @@ import org.cqfn.diktat.api.DiktatProcessorListener import java.io.InputStream import java.io.OutputStream import java.nio.file.Path -import kotlin.io.path.absolutePathString import kotlin.io.path.inputStream /** diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt index ed056830db..4dc1d4a9b7 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractConfigReader.kt @@ -3,15 +3,16 @@ package org.cqfn.diktat.common.config.reader import mu.KotlinLogging import java.io.IOException import java.io.InputStream +import kotlin.jvm.Throws /** * This class is used to read input stream in any format that you will specify. * Usage: * 1) implement this class with implementing the method: * a. parse - implement parser for your file format (for example parse it to a proper json) - * 2) Use your new class MyReader(javaClass.classLoader).read(someInputStream) + * 2) Use your new class MyReader().read(someInputStream) * - * @param - class name parameter that will be used in calculation of classpath + * @param T - class name parameter that will be used in calculation of classpath */ abstract class AbstractConfigReader { /** @@ -30,7 +31,9 @@ abstract class AbstractConfigReader { * * @param inputStream a [InputStream] representing loaded content * @return resource parsed as type [T] + * @throws IOException */ + @Throws(IOException::class) protected abstract fun parse(inputStream: InputStream): T companion object { diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt deleted file mode 100644 index e9d665b45d..0000000000 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/AbstractResourceConfigReader.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.cqfn.diktat.common.config.reader - -import mu.KotlinLogging - -import java.io.IOException -import java.io.InputStream - -/** - * This class is used to read some resource in any format that you will specify. - * Usage: - * 1) implement this class with implementing two methods: - * a. getConfigFile - to return URI (path) to the file that you want to read - * b. parse - implement parser for your file format (for example parse it to a proper json) - * 2) Use your new class MyReader(javaClass.classLoader).readResource("some/path/to/file.format") - * - * @property classLoader The [ClassLoader] used to load the requested resource. - * @param T - class name parameter that will be used in calculation of classpath - */ -abstract class AbstractResourceConfigReader( - protected val classLoader: ClassLoader -) : AbstractConfigReader() { - /** - * @param resourceFileName - related path to a file from resources - * @return object of type [T] if resource has been parsed successfully - */ - fun readResource(resourceFileName: String): T? { - val resourceStream = getConfigFile(resourceFileName) - resourceStream?.let { - try { - return parse(it) - } catch (e: IOException) { - log.error("Cannot read config file $resourceFileName due to: ", e) - } - } - ?: log.error("Not able to open file $resourceFileName from the resources") - return null - } - - /** - * you can override this method in case you would like to read a file not simply from resources - * - * @param resourceFileName name of the resource which will be loaded using [classLoader] - * @return [InputStream] representing loaded resource - */ - protected open fun getConfigFile(resourceFileName: String): InputStream? = classLoader.getResourceAsStream(resourceFileName) - - companion object { - private val log = KotlinLogging.logger {} - } -} diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt index 59515c966c..3d92569263 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt @@ -5,7 +5,7 @@ package org.cqfn.diktat.common.config.rules import org.cqfn.diktat.api.DiktatRuleConfig -import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader +import org.cqfn.diktat.common.config.reader.AbstractConfigReader import org.cqfn.diktat.common.config.rules.RulesConfigReader.Companion.log import com.charleskorn.kaml.Yaml @@ -14,7 +14,6 @@ import com.charleskorn.kaml.decodeFromStream import mu.KLogger import mu.KotlinLogging -import java.io.File import java.io.InputStream import java.util.Locale import java.util.concurrent.atomic.AtomicInteger @@ -57,9 +56,8 @@ open class RuleConfiguration(protected val config: Map) /** * class returns the list of configurations that we have read from a yml: diktat-analysis.yml - * @property classLoader a [ClassLoader] used to load configuration file */ -open class RulesConfigReader(classLoader: ClassLoader) : AbstractResourceConfigReader>(classLoader) { +open class RulesConfigReader : AbstractConfigReader>() { private val yamlSerializer by lazy { Yaml(configuration = YamlConfiguration(strictMode = true)) } /** @@ -70,24 +68,6 @@ open class RulesConfigReader(classLoader: ClassLoader) : AbstractResourceConfigR */ override fun parse(inputStream: InputStream): List = yamlSerializer.decodeFromStream(inputStream) - /** - * instead of reading the resource as it is done in the interface we will read a file by the absolute path here - * if the path is provided, else will read the hardcoded file 'diktat-analysis.yml' from the package - * - * @param resourceFileName name of the resource which will be loaded using [classLoader] - * @return [InputStream] representing loaded resource - */ - override fun getConfigFile(resourceFileName: String): InputStream? { - val resourceFile = File(resourceFileName) - return if (resourceFile.exists()) { - log.debug("Using $DIKTAT_ANALYSIS_CONF file from the following path: ${resourceFile.absolutePath}") - File(resourceFileName).inputStream() - } else { - log.debug("Using the default $DIKTAT_ANALYSIS_CONF file from the class path") - classLoader.getResourceAsStream(resourceFileName) - } - } - companion object { internal val log: KLogger = KotlinLogging.logger {} } diff --git a/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt b/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt index 4d0802e289..3df1b788a4 100644 --- a/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt +++ b/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt @@ -10,8 +10,8 @@ import org.junit.jupiter.api.Test class ConfigReaderTest { @Test fun `testing json reading`() { - val rulesConfigList: List? = RulesConfigReader(javaClass.classLoader) - .readResource("src/test/resources/test-rules-config.yml") + val rulesConfigList: List? = RulesConfigReader() + .read(javaClass.classLoader.getResourceAsStream("src/test/resources/test-rules-config.yml")!!) requireNotNull(rulesConfigList) assert(rulesConfigList.any { it.name == "CLASS_NAME_INCORRECT" && it.enabled }) assert(rulesConfigList.find { it.name == "CLASS_NAME_INCORRECT" }?.configuration == emptyMap()) @@ -21,8 +21,8 @@ class ConfigReaderTest { @Test fun `testing kotlin version`() { - val rulesConfigList: List? = RulesConfigReader(javaClass.classLoader) - .readResource("src/test/resources/test-rules-config.yml") + val rulesConfigList: List? = RulesConfigReader() + .read(javaClass.classLoader.getResourceAsStream("src/test/resources/test-rules-config.yml")!!) requireNotNull(rulesConfigList) assert(rulesConfigList.getCommonConfiguration().kotlinVersion == kotlinVersion) assert(rulesConfigList.getCommonConfiguration().testAnchors.contains("androidUnitTest")) diff --git a/diktat-dev-ksp/build.gradle.kts b/diktat-dev-ksp/build.gradle.kts index db36e832ae..471db06403 100644 --- a/diktat-dev-ksp/build.gradle.kts +++ b/diktat-dev-ksp/build.gradle.kts @@ -6,3 +6,10 @@ plugins { dependencies { implementation(libs.kotlin.ksp.api) } + +tasks.named("diktatFix") { + dependsOn( + tasks.named("compileKotlin"), + tasks.named("processResources"), + ) +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index bf0739f4da..5ed2c32c74 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -17,7 +17,9 @@ import java.io.InputStream * Default implementation for [DiktatRuleConfigReader] */ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { - override fun invoke(inputStream: InputStream): List = RulesConfigReader(javaClass.classLoader) + private val yamlRuleConfigReader = RulesConfigReader() + + override fun invoke(inputStream: InputStream): List = yamlRuleConfigReader .read(inputStream) ?.onEach(::validate) ?: emptyList() diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt index 510910fb77..00ffe81578 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt @@ -108,8 +108,8 @@ class RulesConfigYamlTest { } private fun readAllRulesFromConfig(nameConfig: String) = - RulesConfigReader(javaClass.classLoader) - .readResource(nameConfig) ?: emptyList() + RulesConfigReader() + .read(javaClass.classLoader.getResourceAsStream(nameConfig)!!) ?: emptyList() private fun readAllRulesFromCode() = Warnings.values() diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt index fbadd9f3f6..2bc3702656 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/DiktatRuleSetFactoryImplTest.kt @@ -63,8 +63,8 @@ class DiktatRuleSetFactoryImplTest { ruleSupplier: (rulesConfigList: List) -> DiktatRule, rulesConfigList: List?, ): DiktatRuleSet = run { - rulesConfigList ?: RulesConfigReader(Companion::class.java.classLoader) - .readResource("diktat-analysis.yml") + rulesConfigList ?: Companion::class.java.classLoader.getResourceAsStream("diktat-analysis.yml") + ?.let { RulesConfigReader().read(it) } .orEmpty() } .let(ruleSupplier) 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 0b0a1cf580..ed8f8a6874 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 @@ -21,6 +21,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_CLASS_ELEMENTS import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTION import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION +import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming import org.cqfn.diktat.ruleset.rules.chapter2.comments.CommentsRule import org.cqfn.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule @@ -68,7 +69,7 @@ abstract class DiktatSmokeTestBase { */ @Suppress("UnsafeCallOnNullableType") private fun prepareOverriddenRulesConfig(rulesToDisable: List = emptyList(), rulesToOverride: RuleToConfig = emptyMap()): Path { - val rulesConfig = RulesConfigReader(javaClass.classLoader).readResource(DEFAULT_CONFIG_PATH)!! + val rulesConfig = RulesConfigReader().read(DiktatRuleConfigReaderImpl.readConfigFile(DEFAULT_CONFIG_PATH))!! .toMutableList() .also { rulesConfig -> rulesToDisable.forEach { warning -> diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt index e27049ddcb..3219c87974 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestArgumentsReader.kt @@ -1,7 +1,7 @@ package org.cqfn.diktat.test.framework.config import org.cqfn.diktat.common.cli.CliArgument -import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader +import org.cqfn.diktat.common.config.reader.AbstractConfigReader import mu.KotlinLogging import org.apache.commons.cli.CommandLine @@ -22,16 +22,16 @@ import kotlinx.serialization.json.decodeFromStream /** * Class that gives access to properties of a test * + * @param classLoader [ClassLoader] which is used to load properties file * @property args CLI arguments * @property properties properties from properties file - * @property classLoader [ClassLoader] which is used to load properties file */ class TestArgumentsReader( private val args: Array, val properties: TestFrameworkProperties, classLoader: ClassLoader -) : AbstractResourceConfigReader>(classLoader) { - private val cliArguments: List? = readResource(properties.testFrameworkArgsRelativePath) +) : AbstractConfigReader>() { + private val cliArguments: List? = classLoader.getResourceAsStream(properties.testFrameworkArgsRelativePath)?.let { read(it) } private val cmd: CommandLine by lazy { parseArguments() } /** diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt index 8e01ed8675..4d601a3824 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/config/TestConfigReader.kt @@ -1,6 +1,6 @@ package org.cqfn.diktat.test.framework.config -import org.cqfn.diktat.common.config.reader.AbstractResourceConfigReader +import org.cqfn.diktat.common.config.reader.AbstractConfigReader import java.io.IOException import java.io.InputStream @@ -10,14 +10,13 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream /** - * A [AbstractResourceConfigReader] to read tests configuration as [TestConfig] - * @property classLoader a [ClassLoader] to load configuration file + * A [AbstractConfigReader] to read tests configuration as [TestConfig] */ -class TestConfigReader(configFilePath: String, classLoader: ClassLoader) : AbstractResourceConfigReader(classLoader) { +class TestConfigReader(configFilePath: String, classLoader: ClassLoader) : AbstractConfigReader() { /** * The [TestConfig] which is read from */ - val config: TestConfig? = readResource(configFilePath) + val config: TestConfig? = classLoader.getResourceAsStream(configFilePath)?.let { read(it) } /** * @param inputStream input stream of data from config file From 9f75bf0e6aac74e539925d813b32349f4a9ccd0a Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 16:31:42 +0300 Subject: [PATCH 10/19] fixed diktatCheck too --- diktat-api/build.gradle.kts | 14 ++++++++------ diktat-dev-ksp/build.gradle.kts | 12 +++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/diktat-api/build.gradle.kts b/diktat-api/build.gradle.kts index f04aabfe18..eb76dd5e81 100644 --- a/diktat-api/build.gradle.kts +++ b/diktat-api/build.gradle.kts @@ -42,10 +42,12 @@ kotlin.sourceSets.getByName("main") { ) } -tasks.named("diktatFix") { - dependsOn( - generateDiktatVersionFile, - tasks.named("compileKotlin"), - tasks.named("processResources"), - ) +sequenceOf("diktatFix", "diktatCheck").forEach { diktatTaskName -> + tasks.named(diktatTaskName) { + dependsOn( + generateDiktatVersionFile, + tasks.named("compileKotlin"), + tasks.named("processResources"), + ) + } } diff --git a/diktat-dev-ksp/build.gradle.kts b/diktat-dev-ksp/build.gradle.kts index 471db06403..9ba5d79202 100644 --- a/diktat-dev-ksp/build.gradle.kts +++ b/diktat-dev-ksp/build.gradle.kts @@ -7,9 +7,11 @@ dependencies { implementation(libs.kotlin.ksp.api) } -tasks.named("diktatFix") { - dependsOn( - tasks.named("compileKotlin"), - tasks.named("processResources"), - ) +sequenceOf("diktatFix", "diktatCheck").forEach { diktatTaskName -> + tasks.named(diktatTaskName) { + dependsOn( + tasks.named("compileKotlin"), + tasks.named("processResources"), + ) + } } From 0fd3005f04ff3d4eaa4cfc9fa6d31599dd6c69a8 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 16:37:36 +0300 Subject: [PATCH 11/19] a small fixes --- .../cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt | 6 ++---- .../org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt | 5 +++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index 5ed2c32c74..f34fc5b8b4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -32,8 +32,6 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { companion object { private val log = KotlinLogging.logger {} - @Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") - private val javaClass = object {}.javaClass /** * @param diktatConfigFile the configuration file where all configurations for @@ -48,7 +46,7 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { resourceFile.inputStream() } else { log.debug { "Using the default $DIKTAT_ANALYSIS_CONF file from the class path" } - javaClass.classLoader.getResourceAsStream(resourceFileName) ?: run { + Companion::class.java.classLoader.getResourceAsStream(resourceFileName) ?: run { log.error { "Not able to open file $resourceFileName from the resources" } object : InputStream() { override fun read(): Int = -1 @@ -89,7 +87,7 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { private fun resolveConfigFileFromJarLocation(diktatConfigFile: String): String { // 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 = javaClass + val ruleSetProviderPath = Companion::class.java .protectionDomain .codeSource .location diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt index 00ffe81578..9e9187b29d 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt @@ -108,8 +108,9 @@ class RulesConfigYamlTest { } private fun readAllRulesFromConfig(nameConfig: String) = - RulesConfigReader() - .read(javaClass.classLoader.getResourceAsStream(nameConfig)!!) ?: emptyList() + javaClass.classLoader.getResourceAsStream(nameConfig)?.let { + RulesConfigReader().read(it) + } ?: emptyList() private fun readAllRulesFromCode() = Warnings.values() From 4fac411283b03a407fc87b82aad57485bbf4b7c3 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 16:49:16 +0300 Subject: [PATCH 12/19] moved logic with loading config from different location to SPI --- .../org/cqfn/diktat/test/ConfigReaderTest.kt | 6 +- .../rules/DiktatRuleConfigReaderImpl.kt | 79 ------------------- .../rules/DiktatRuleSetProviderV3Spi.kt | 76 +++++++++++++++++- .../ruleset/smoke/DiktatSmokeTestBase.kt | 5 +- 4 files changed, 82 insertions(+), 84 deletions(-) diff --git a/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt b/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt index 3df1b788a4..f56124a957 100644 --- a/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt +++ b/diktat-common/src/test/kotlin/org/cqfn/diktat/test/ConfigReaderTest.kt @@ -6,12 +6,14 @@ import org.cqfn.diktat.common.config.rules.RulesConfigReader import org.cqfn.diktat.common.config.rules.getCommonConfiguration import org.cqfn.diktat.common.config.rules.kotlinVersion import org.junit.jupiter.api.Test +import java.nio.file.Paths +import kotlin.io.path.inputStream class ConfigReaderTest { @Test fun `testing json reading`() { val rulesConfigList: List? = RulesConfigReader() - .read(javaClass.classLoader.getResourceAsStream("src/test/resources/test-rules-config.yml")!!) + .read(Paths.get("src/test/resources/test-rules-config.yml").inputStream()) requireNotNull(rulesConfigList) assert(rulesConfigList.any { it.name == "CLASS_NAME_INCORRECT" && it.enabled }) assert(rulesConfigList.find { it.name == "CLASS_NAME_INCORRECT" }?.configuration == emptyMap()) @@ -22,7 +24,7 @@ class ConfigReaderTest { @Test fun `testing kotlin version`() { val rulesConfigList: List? = RulesConfigReader() - .read(javaClass.classLoader.getResourceAsStream("src/test/resources/test-rules-config.yml")!!) + .read(Paths.get("src/test/resources/test-rules-config.yml").inputStream()) requireNotNull(rulesConfigList) assert(rulesConfigList.getCommonConfiguration().kotlinVersion == kotlinVersion) assert(rulesConfigList.getCommonConfiguration().testAnchors.contains("androidUnitTest")) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index f34fc5b8b4..df885f7eda 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -2,15 +2,10 @@ package org.cqfn.diktat.ruleset.rules import org.cqfn.diktat.api.DiktatRuleConfig import org.cqfn.diktat.api.DiktatRuleConfigReader -import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON -import org.cqfn.diktat.common.config.rules.DIKTAT_CONF_PROPERTY -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.common.config.rules.RulesConfigReader import org.cqfn.diktat.ruleset.constants.Warnings -import mu.KotlinLogging import org.jetbrains.kotlin.org.jline.utils.Levenshtein -import java.io.File import java.io.InputStream /** @@ -29,78 +24,4 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { val closestMatch = Warnings.names.minByOrNull { Levenshtein.distance(it, config.name) } "Warning name <${config.name}> in configuration file is invalid, did you mean <$closestMatch>?" } - - companion object { - private val log = KotlinLogging.logger {} - - /** - * @param diktatConfigFile the configuration file where all configurations for - * inspections and rules are stored. - * @return resolved existed diktatConfigFile - */ - fun readConfigFile(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): InputStream { - val resourceFileName = resolveConfigFile(diktatConfigFile) - val resourceFile = File(resourceFileName) - return if (resourceFile.exists()) { - log.debug { "Using $DIKTAT_ANALYSIS_CONF file from the following path: ${resourceFile.absolutePath}" } - resourceFile.inputStream() - } else { - log.debug { "Using the default $DIKTAT_ANALYSIS_CONF file from the class path" } - Companion::class.java.classLoader.getResourceAsStream(resourceFileName) ?: run { - log.error { "Not able to open file $resourceFileName from the resources" } - object : InputStream() { - override fun read(): Int = -1 - } - } - } - } - - private fun resolveConfigFile(diktatConfigFile: String = DIKTAT_ANALYSIS_CONF): String { - val possibleConfigs: Sequence = sequence { - yield(resolveDefaultConfig(diktatConfigFile)) - yield(resolveConfigFileFromJarLocation(diktatConfigFile)) - yield(resolveConfigFileFromSystemProperty()) - } - - log.debug("Will run $DIKTAT_RULE_SET_ID with $diktatConfigFile" + - " (it can be placed to the run directory or the default file from resources will be used)") - val configPath = possibleConfigs - .firstOrNull { it != null && File(it).exists() } - return configPath - ?: run { - val possibleConfigsList = possibleConfigs.toList() - log.warn( - "Configuration file not found in directory where diktat is run (${possibleConfigsList[0]}) " + - "or in the directory where diktat.jar is stored (${possibleConfigsList[1]}) " + - "or in system property (${possibleConfigsList[2]}), " + - "the default file included in jar will be used. " + - "Some configuration options will be disabled or substituted with defaults. " + - "Custom configuration file should be placed in diktat working directory if run from CLI " + - "or provided as configuration options in plugins." - ) - diktatConfigFile - } - } - - private fun resolveDefaultConfig(diktatConfigFile: String) = diktatConfigFile - - private fun resolveConfigFileFromJarLocation(diktatConfigFile: String): String { - // 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 = Companion::class.java - .protectionDomain - .codeSource - .location - .toURI() - - val configPathWithFileName = File(ruleSetProviderPath).absolutePath - - val indexOfName = configPathWithFileName.lastIndexOf(File.separator) - val configPath = if (indexOfName > -1) configPathWithFileName.substring(0, indexOfName) else configPathWithFileName - - return "$configPath${File.separator}$diktatConfigFile" - } - - private fun resolveConfigFileFromSystemProperty(): String? = System.getProperty(DIKTAT_CONF_PROPERTY) - } } diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt index f41776dc3b..94a6520af8 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt @@ -1,5 +1,7 @@ package org.cqfn.diktat.ruleset.rules +import org.cqfn.diktat.common.config.rules.DIKTAT_ANALYSIS_CONF +import org.cqfn.diktat.common.config.rules.DIKTAT_CONF_PROPERTY import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.ktlint.KtLintRuleWrapper.Companion.toKtLint import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 @@ -8,6 +10,8 @@ import com.pinterest.ktlint.rule.engine.core.api.RuleProvider import com.pinterest.ktlint.rule.engine.core.api.RuleSetId import mu.KotlinLogging import org.slf4j.Logger +import java.io.File +import java.io.InputStream /** * [RuleSetProviderV3] that provides diKTat ruleset. @@ -30,6 +34,76 @@ class DiktatRuleSetProviderV3Spi : RuleSetProviderV3( KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).initKtLintKLogger() } - override fun getRuleProviders(): Set = diktatRuleSetFactory(diktatRuleConfigReader(DiktatRuleConfigReaderImpl.readConfigFile())) + override fun getRuleProviders(): Set = diktatRuleSetFactory(diktatRuleConfigReader(readConfigFile())) .toKtLint() + + private fun readConfigFile(): InputStream { + val resourceFileName = resolveConfigFile() + val resourceFile = File(resourceFileName) + return if (resourceFile.exists()) { + log.debug { "Using $DIKTAT_ANALYSIS_CONF file from the following path: ${resourceFile.absolutePath}" } + resourceFile.inputStream() + } else { + log.debug { "Using the default $DIKTAT_ANALYSIS_CONF file from the class path" } + DiktatRuleConfigReaderImpl.Companion::class.java.classLoader.getResourceAsStream(resourceFileName) ?: run { + log.error { "Not able to open file $resourceFileName from the resources" } + object : InputStream() { + override fun read(): Int = -1 + } + } + } + } + + private fun resolveConfigFile(): String { + val possibleConfigs: Sequence = sequence { + yield(DIKTAT_ANALYSIS_CONF) + yield(resolveConfigFileFromJarLocation()) + yield(resolveConfigFileFromSystemProperty()) + } + + log.debug("Will run $DIKTAT_RULE_SET_ID with $DIKTAT_ANALYSIS_CONF" + + " (it can be placed to the run directory or the default file from resources will be used)") + val configPath = possibleConfigs + .firstOrNull { it != null && File(it).exists() } + return configPath + ?: run { + val possibleConfigsList = possibleConfigs.toList() + log.warn( + "Configuration file not found in directory where diktat is run (${possibleConfigsList[0]}) " + + "or in the directory where diktat.jar is stored (${possibleConfigsList[1]}) " + + "or in system property (${possibleConfigsList[2]}), " + + "the default file included in jar will be used. " + + "Some configuration options will be disabled or substituted with defaults. " + + "Custom configuration file should be placed in diktat working directory if run from CLI " + + "or provided as configuration options in plugins." + ) + DIKTAT_ANALYSIS_CONF + } + } + + private fun resolveConfigFileFromJarLocation(): String { + // 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 = DiktatRuleConfigReaderImpl.Companion::class.java + .protectionDomain + .codeSource + .location + .toURI() + + val configPathWithFileName = File(ruleSetProviderPath).absolutePath + + val indexOfName = configPathWithFileName.lastIndexOf(File.separator) + val configPath = if (indexOfName > -1) configPathWithFileName.substring(0, indexOfName) else configPathWithFileName + + return "$configPath${File.separator}$DIKTAT_ANALYSIS_CONF" + } + + private fun resolveConfigFileFromSystemProperty(): String? = System.getProperty(DIKTAT_CONF_PROPERTY) + + companion object { + // need to load after [com.pinterest.ktlint.logger.api.initKtLintKLogger] + private val log by lazy { + KotlinLogging.logger {} + } + } } 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 ed8f8a6874..6f8ac37bd8 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 @@ -21,7 +21,6 @@ import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_CLASS_ELEMENTS import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTION import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION -import org.cqfn.diktat.ruleset.rules.DiktatRuleConfigReaderImpl import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming import org.cqfn.diktat.ruleset.rules.chapter2.comments.CommentsRule import org.cqfn.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule @@ -47,10 +46,12 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout import java.io.File import java.nio.file.Path +import java.nio.file.Paths import java.time.LocalDate import java.util.concurrent.TimeUnit.SECONDS import kotlin.io.path.createTempFile +import kotlin.io.path.inputStream import kotlin.io.path.writeText import kotlinx.serialization.builtins.ListSerializer @@ -69,7 +70,7 @@ abstract class DiktatSmokeTestBase { */ @Suppress("UnsafeCallOnNullableType") private fun prepareOverriddenRulesConfig(rulesToDisable: List = emptyList(), rulesToOverride: RuleToConfig = emptyMap()): Path { - val rulesConfig = RulesConfigReader().read(DiktatRuleConfigReaderImpl.readConfigFile(DEFAULT_CONFIG_PATH))!! + val rulesConfig = RulesConfigReader().read(Paths.get(DEFAULT_CONFIG_PATH).inputStream())!! .toMutableList() .also { rulesConfig -> rulesToDisable.forEach { warning -> From ddea0698dd2cd3eee0a7ff4f2d9af9a3cc66c406 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 17:02:39 +0300 Subject: [PATCH 13/19] fixed compilation error --- .../cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt index 94a6520af8..b9b1e5a2ad 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt @@ -45,7 +45,7 @@ class DiktatRuleSetProviderV3Spi : RuleSetProviderV3( resourceFile.inputStream() } else { log.debug { "Using the default $DIKTAT_ANALYSIS_CONF file from the class path" } - DiktatRuleConfigReaderImpl.Companion::class.java.classLoader.getResourceAsStream(resourceFileName) ?: run { + javaClass.classLoader.getResourceAsStream(resourceFileName) ?: run { log.error { "Not able to open file $resourceFileName from the resources" } object : InputStream() { override fun read(): Int = -1 @@ -84,7 +84,7 @@ class DiktatRuleSetProviderV3Spi : RuleSetProviderV3( private fun resolveConfigFileFromJarLocation(): String { // 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 = DiktatRuleConfigReaderImpl.Companion::class.java + val ruleSetProviderPath = javaClass .protectionDomain .codeSource .location From 83887230cdb58437d13e0151b993b3d042a571e6 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 17:04:00 +0300 Subject: [PATCH 14/19] no need to create log lazy --- .../cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt index b9b1e5a2ad..b86f72521a 100644 --- a/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt +++ b/diktat-ruleset/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProviderV3Spi.kt @@ -101,9 +101,6 @@ class DiktatRuleSetProviderV3Spi : RuleSetProviderV3( private fun resolveConfigFileFromSystemProperty(): String? = System.getProperty(DIKTAT_CONF_PROPERTY) companion object { - // need to load after [com.pinterest.ktlint.logger.api.initKtLintKLogger] - private val log by lazy { - KotlinLogging.logger {} - } + private val log = KotlinLogging.logger {} } } From 56279cd0b458c962632fea4d73df8fa59f3a3ab9 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 8 Jun 2023 17:31:19 +0300 Subject: [PATCH 15/19] diktatFix + fixed test --- .../diktat/common/config/rules/RulesConfigReader.kt | 4 ++-- .../diktat/ruleset/utils/RulesConfigYamlTest.kt | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt index 3d92569263..ffaf404fda 100644 --- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt +++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt @@ -18,8 +18,6 @@ import java.io.InputStream import java.util.Locale import java.util.concurrent.atomic.AtomicInteger -typealias RulesConfig = DiktatRuleConfig - /** * Name of common configuration */ @@ -38,6 +36,8 @@ const val DIKTAT_RULE_SET_ID = "diktat-ruleset" const val DIKTAT_ANALYSIS_CONF = "diktat-analysis.yml" const val DIKTAT_CONF_PROPERTY = "diktat.config.path" +typealias RulesConfig = DiktatRuleConfig + /** * This interface represents individual inspection in rule set. */ diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt index 9e9187b29d..e2a1441fc9 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/RulesConfigYamlTest.kt @@ -12,6 +12,9 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import java.io.File +import java.nio.file.Paths +import kotlin.io.path.exists +import kotlin.io.path.inputStream import kotlinx.serialization.encodeToString @@ -107,10 +110,12 @@ class RulesConfigYamlTest { } } - private fun readAllRulesFromConfig(nameConfig: String) = - javaClass.classLoader.getResourceAsStream(nameConfig)?.let { - RulesConfigReader().read(it) - } ?: emptyList() + private fun readAllRulesFromConfig(nameConfig: String) = run { + Paths.get(nameConfig).takeIf { it.exists() }?.inputStream() + ?: javaClass.classLoader.getResourceAsStream(nameConfig) + } + ?.let { RulesConfigReader().read(it) } + ?: emptyList() private fun readAllRulesFromCode() = Warnings.values() From 16352bc7eb968e9c9ad28d0697ea9c20c7f9cc0d Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 9 Jun 2023 14:09:23 +0300 Subject: [PATCH 16/19] fixed review notes --- .../org/cqfn/diktat/api/DiktatRuleConfigReader.kt | 2 +- .../org/cqfn/diktat/api/DiktatRuleSetFactory.kt | 2 +- .../org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt | 14 +++++++++----- .../ruleset/smoke/RulesConfigValidationTest.kt | 11 ++++++++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt index 77eb233de1..782e414694 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleConfigReader.kt @@ -5,7 +5,7 @@ import java.io.InputStream /** * A reader for [DiktatRuleConfig] */ -interface DiktatRuleConfigReader : Function1> { +fun interface DiktatRuleConfigReader : Function1> { /** * @param inputStream * @return parsed [DiktatRuleConfig]s diff --git a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt index 255d99ee8d..a1caec2050 100644 --- a/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt +++ b/diktat-api/src/main/kotlin/org/cqfn/diktat/api/DiktatRuleSetFactory.kt @@ -3,7 +3,7 @@ package org.cqfn.diktat.api /** * A factory which creates a [DiktatRuleSet]. */ -interface DiktatRuleSetFactory : Function1, DiktatRuleSet> { +fun interface DiktatRuleSetFactory : Function1, DiktatRuleSet> { /** * @param rulesConfig all configurations for rules * @return the default instance of [DiktatRuleSet] 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 ef9e145f83..44ce7f2ebb 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 @@ -19,7 +19,11 @@ import org.apache.maven.project.MavenProject import java.io.File import java.io.FileOutputStream import java.io.OutputStream +import java.nio.file.Path +import java.nio.file.Paths import kotlin.io.path.Path +import kotlin.io.path.inputStream +import kotlin.io.path.isRegularFile /** * Base [Mojo] for checking and fixing code using diktat @@ -95,7 +99,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { */ override fun execute() { val configFile = resolveConfig() - if (!configFile.exists()) { + if (configFile.isRegularFile()) { throw MojoExecutionException("Configuration file $diktatConfigFile doesn't exist") } log.info("Running diKTat plugin with configuration file $configFile and inputs $inputs" + @@ -152,16 +156,16 @@ abstract class DiktatBaseMojo : AbstractMojo() { * * @return a configuration file. File by this path might not exist. */ - private fun resolveConfig(): File { - val file = File(diktatConfigFile) + private fun resolveConfig(): Path { + val file = Paths.get(diktatConfigFile) if (file.isAbsolute) { return file } return generateSequence(mavenProject) { it.parent } - .map { File(it.basedir, diktatConfigFile) } + .map { it.basedir.toPath().resolve(diktatConfigFile) } .run { - firstOrNull { it.exists() } ?: first() + firstOrNull { it.isRegularFile() } ?: first() } } } 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 346efb53aa..24e91ef5c2 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 @@ -40,7 +40,7 @@ class RulesConfigValidationTest { """.trimMargin() ) val exception = assertThrows { - DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(file.inputStream())) + diktatRuleSetFactory(diktatRuleConfigReader(file.inputStream())) } Assertions.assertEquals("Warning name in configuration file is invalid, did you mean ?", exception.message) } @@ -55,7 +55,7 @@ class RulesConfigValidationTest { """.trimMargin() ) assertThrows { - DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(file.inputStream())) + diktatRuleSetFactory(diktatRuleConfigReader(file.inputStream())) } } @@ -71,6 +71,11 @@ class RulesConfigValidationTest { | isIncludeHeader: Fslse """.trimMargin() ) - DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(file.inputStream())) + diktatRuleSetFactory(diktatRuleConfigReader(file.inputStream())) + } + + companion object { + private val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() + private val diktatRuleConfigReader = DiktatRuleConfigReaderImpl() } } From 0b599ac0e818841b56de3d39ffec239371f7d292 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 13 Jun 2023 12:52:36 +0300 Subject: [PATCH 17/19] fixed packages after merging the master --- diktat-api/build.gradle.kts | 12 +++++------- .../saveourtool/diktat/api/DiktatRuleConfig.kt | 2 +- .../diktat/api/DiktatRuleConfigReader.kt | 3 ++- .../common/config/reader/AbstractConfigReader.kt | 2 +- diktat-dev-ksp/build.gradle.kts | 10 ++++------ .../ruleset/rules/DiktatRuleConfigReaderImpl.kt | 14 +++++++------- .../diktat/util/DiktatRuleSetFactoryImplTest.kt | 16 +--------------- 7 files changed, 21 insertions(+), 38 deletions(-) diff --git a/diktat-api/build.gradle.kts b/diktat-api/build.gradle.kts index 0934f24115..ed59aeca9e 100644 --- a/diktat-api/build.gradle.kts +++ b/diktat-api/build.gradle.kts @@ -43,11 +43,9 @@ kotlin.sourceSets.getByName("main") { } sequenceOf("diktatFix", "diktatCheck").forEach { diktatTaskName -> - tasks.named(diktatTaskName) { - dependsOn( - generateDiktatVersionFile, - tasks.named("compileKotlin"), - tasks.named("processResources"), - ) - } + tasks.findByName(diktatTaskName)?.dependsOn( + generateDiktatVersionFile, + tasks.named("compileKotlin"), + tasks.named("processResources"), + ) } diff --git a/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfig.kt b/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfig.kt index 39458e7c18..1de4d90f87 100644 --- a/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfig.kt +++ b/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfig.kt @@ -1,4 +1,4 @@ -package org.cqfn.diktat.api +package com.saveourtool.diktat.api import kotlinx.serialization.Serializable diff --git a/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt b/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt index 782e414694..41232431f3 100644 --- a/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt +++ b/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt @@ -1,5 +1,6 @@ -package org.cqfn.diktat.api +package com.saveourtool.diktat.api +import com.saveourtool.diktat.api.DiktatRuleConfig import java.io.InputStream /** diff --git a/diktat-common/src/main/kotlin/com/saveourtool/diktat/common/config/reader/AbstractConfigReader.kt b/diktat-common/src/main/kotlin/com/saveourtool/diktat/common/config/reader/AbstractConfigReader.kt index 4dc1d4a9b7..32b15e18a5 100644 --- a/diktat-common/src/main/kotlin/com/saveourtool/diktat/common/config/reader/AbstractConfigReader.kt +++ b/diktat-common/src/main/kotlin/com/saveourtool/diktat/common/config/reader/AbstractConfigReader.kt @@ -1,4 +1,4 @@ -package org.cqfn.diktat.common.config.reader +package com.saveourtool.diktat.common.config.reader import mu.KotlinLogging import java.io.IOException diff --git a/diktat-dev-ksp/build.gradle.kts b/diktat-dev-ksp/build.gradle.kts index b5b8b78e1b..9b2228cdf6 100644 --- a/diktat-dev-ksp/build.gradle.kts +++ b/diktat-dev-ksp/build.gradle.kts @@ -8,10 +8,8 @@ dependencies { } sequenceOf("diktatFix", "diktatCheck").forEach { diktatTaskName -> - tasks.named(diktatTaskName) { - dependsOn( - tasks.named("compileKotlin"), - tasks.named("processResources"), - ) - } + tasks.findByName(diktatTaskName)?.dependsOn( + tasks.named("compileKotlin"), + tasks.named("processResources"), + ) } diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt index df885f7eda..fa12a80381 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/DiktatRuleConfigReaderImpl.kt @@ -1,10 +1,10 @@ -package org.cqfn.diktat.ruleset.rules +package com.saveourtool.diktat.ruleset.rules -import org.cqfn.diktat.api.DiktatRuleConfig -import org.cqfn.diktat.api.DiktatRuleConfigReader -import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON -import org.cqfn.diktat.common.config.rules.RulesConfigReader -import org.cqfn.diktat.ruleset.constants.Warnings +import com.saveourtool.diktat.api.DiktatRuleConfig +import com.saveourtool.diktat.api.DiktatRuleConfigReader +import com.saveourtool.diktat.common.config.rules.DIKTAT_COMMON +import com.saveourtool.diktat.common.config.rules.RulesConfigReader +import com.saveourtool.diktat.ruleset.constants.Warnings import org.jetbrains.kotlin.org.jline.utils.Levenshtein import java.io.InputStream @@ -19,7 +19,7 @@ class DiktatRuleConfigReaderImpl : DiktatRuleConfigReader { ?.onEach(::validate) ?: emptyList() - private fun validate(config: org.cqfn.diktat.common.config.rules.RulesConfig) = + private fun validate(config: com.saveourtool.diktat.common.config.rules.RulesConfig) = require(config.name == DIKTAT_COMMON || config.name in Warnings.names) { val closestMatch = Warnings.names.minByOrNull { Levenshtein.distance(it, config.name) } "Warning name <${config.name}> in configuration file is invalid, did you mean <$closestMatch>?" diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetFactoryImplTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetFactoryImplTest.kt index c244d152d6..201e98c8d9 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetFactoryImplTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetFactoryImplTest.kt @@ -4,21 +4,12 @@ package com.saveourtool.diktat.util -<<<<<<<< HEAD:diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetFactoryImplTest.kt -import org.cqfn.diktat.api.DiktatRuleSet -import org.cqfn.diktat.common.config.rules.RulesConfig -import org.cqfn.diktat.common.config.rules.RulesConfigReader -import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetFactoryImpl -import org.cqfn.diktat.test.framework.util.filterContentMatches -======== import com.saveourtool.diktat.api.DiktatRuleSet import com.saveourtool.diktat.common.config.rules.RulesConfig import com.saveourtool.diktat.common.config.rules.RulesConfigReader import com.saveourtool.diktat.ruleset.rules.DiktatRule -import com.saveourtool.diktat.ruleset.rules.DiktatRuleSetProvider +import com.saveourtool.diktat.ruleset.rules.DiktatRuleSetFactoryImpl import com.saveourtool.diktat.test.framework.util.filterContentMatches ->>>>>>>> origin/master:diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetProviderTest.kt import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -34,13 +25,8 @@ class DiktatRuleSetFactoryImplTest { @OptIn(ExperimentalPathApi::class) @Suppress("UnsafeCallOnNullableType") @Test -<<<<<<<< HEAD:diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetFactoryImplTest.kt fun `check DiktatRuleSetFactoryImpl contain all rules`() { - val path = "${System.getProperty("user.dir")}/src/main/kotlin/org/cqfn/diktat/ruleset/rules" -======== - fun `check DiktatRuleSetProviderTest contain all rules`() { val path = "${System.getProperty("user.dir")}/src/main/kotlin/com/saveourtool/diktat/ruleset/rules" ->>>>>>>> origin/master:diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/DiktatRuleSetProviderTest.kt val fileNames = Path(path) .walk() .filter(Path::isRegularFile) From 2bb0f19da0819a2d766417c8eace5accfc6ed69b Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 13 Jun 2023 13:26:29 +0300 Subject: [PATCH 18/19] diktatFix --- .../kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt b/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt index 41232431f3..c10c102f51 100644 --- a/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt +++ b/diktat-api/src/main/kotlin/com/saveourtool/diktat/api/DiktatRuleConfigReader.kt @@ -1,6 +1,5 @@ package com.saveourtool.diktat.api -import com.saveourtool.diktat.api.DiktatRuleConfig import java.io.InputStream /** From 04294d8f622bcf5eee4fee83f0a273f28110cabd Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 13 Jun 2023 13:31:10 +0300 Subject: [PATCH 19/19] avoid ().invoke() --- .../com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt index 1865c72167..01874baabc 100644 --- a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -43,7 +43,11 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { resourceFilePath = RESOURCE_FILE_PATH, function = { testFile -> format( - ruleSetSupplier = { DiktatRuleSetFactoryImpl().invoke(DiktatRuleConfigReaderImpl().invoke(config.inputStream())) }, + ruleSetSupplier = { + val diktatRuleConfigReader = DiktatRuleConfigReaderImpl() + val diktatRuleSetFactory = DiktatRuleSetFactoryImpl() + diktatRuleSetFactory(diktatRuleConfigReader(config.inputStream())) + }, file = testFile, cb = { lintError, _ -> unfixedLintErrors.add(lintError) }, )