All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider.kt Maven / Gradle / Ivy

There is a newer version: 1.2.5
Show newest version
@file:Suppress(
    "Deprecation"
)

package org.cqfn.diktat.ruleset.rules

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.common.utils.loggerWithKtlintConfig
import org.cqfn.diktat.ruleset.constants.Warnings
import org.cqfn.diktat.ruleset.rules.OrderedRuleSet.Companion.ordered
import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming
import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming
import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming
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 com.pinterest.ktlint.core.RuleSet
import com.pinterest.ktlint.core.RuleSetProvider
import mu.KotlinLogging
import org.jetbrains.kotlin.org.jline.utils.Levenshtein

import java.io.File

/**
 * [RuleSetProvider] that provides diKTat ruleset.
 * By default, it is expected to have diktat-analysis.yml configuration in the root folder where 'ktlint' is run
 * otherwise it will use default configuration where some rules are disabled
 *
 * @param diktatConfigFile - configuration file where all configurations for inspections and rules are stored
 */
class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) : RuleSetProvider {
    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() }
        diktatConfigFile = 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(diktatConfigFile)
            ?.onEach(::validate)
            ?: emptyList()
    }

    @Suppress(
        "LongMethod",
        "TOO_LONG_FUNCTION",
    )
    @Deprecated(
        "Marked for removal in KtLint 0.48. See changelog or KDoc for more information.",
    )
    override fun get(): RuleSet {
        // Note: the order of rules is important in autocorrect mode. For example, all rules that add new code should be invoked before rules that fix formatting.
        // We don't have a way to enforce a specific order, so we should just be careful when adding new rules to this list and, when possible,
        // cover new rules in smoke test as well. If a rule needs to be at a specific position in a list, please add comment explaining it (like for NewlinesRule).
        val rules = listOf(
            // 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.invoke(configRules)
            }
        return RuleSet(
            DIKTAT_RULE_SET_ID,
            rules = rules.toTypedArray()
        ).ordered()
    }

    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 =
            DiktatRuleSetProvider::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)

    companion object {
        private val log = KotlinLogging.loggerWithKtlintConfig(DiktatRuleSetProvider::class)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy