Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feature/diktat-command
Browse files Browse the repository at this point in the history
  • Loading branch information
nulls committed Mar 30, 2023
2 parents 0600a2a + 08f3e2a commit 4ee2417
Show file tree
Hide file tree
Showing 17 changed files with 236 additions and 190 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.cqfn.diktat.ktlint

import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint
import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider
import com.pinterest.ktlint.core.RuleSet
import com.pinterest.ktlint.core.RuleSetProvider

/**
* This is a wrapper around __KtLint__'s [RuleSetProvider].
*/
class KtLintRuleSetProviderWrapper private constructor(
private val diktatRuleSetFactory: DiktatRuleSetProvider,
) : RuleSetProvider {
override fun get(): RuleSet = diktatRuleSetFactory().toKtLint()

companion object {
/**
* @return __KtLint__'s [RuleSetProvider] created from [DiktatRuleSetProvider]
*/
fun DiktatRuleSetProvider.toKtLint(): RuleSetProvider = KtLintRuleSetProviderWrapper(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.cqfn.diktat.ktlint

import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.rules.DiktatRuleSet
import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.RuleSet

/**
* This is a wrapper around __KtLint__'s [RuleSet] which adjusts visitorModifiers for all rules to keep order with prevRule
* Added as a workaround after introducing a new logic for sorting KtLint Rules: https://github.com/pinterest/ktlint/issues/1478
*
* @param diktatRuleSet the rules which belong to the current [DiktatRuleSet].
*/
class KtLintRuleSetWrapper private constructor(
diktatRuleSet: DiktatRuleSet,
) : RuleSet(DIKTAT_RULE_SET_ID, rules = wrapRules(diktatRuleSet.rules)) {
companion object {
/**
* @return __KtLint__'s [RuleSet] created from [DiktatRuleSet]
*/
fun DiktatRuleSet.toKtLint(): RuleSet = KtLintRuleSetWrapper(this)

private fun wrapRules(rules: List<DiktatRule>): Array<Rule> {
if (rules.isEmpty()) {
return emptyArray()
}
return rules.runningFold(null as KtLintRuleWrapper?) { prevRule, diktatRule ->
KtLintRuleWrapper(diktatRule, prevRule)
}
.filterNotNull()
.toTypedArray()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.cqfn.diktat.ktlint

import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID
import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.rules.DiktatRule
import com.pinterest.ktlint.core.Rule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

/**
* This is a wrapper around __KtLint__'s [Rule] which adjusts visitorModifiers to keep order with prevRule.
* @property rule
*/
class KtLintRuleWrapper(
val rule: DiktatRule,
prevRule: KtLintRuleWrapper? = null,
) : Rule(
id = rule.id.qualifiedWithRuleSetId(DIKTAT_RULE_SET_ID),
visitorModifiers = createVisitorModifiers(rule, prevRule),
) {
@Deprecated(
"Marked for deletion in ktlint 0.48.0",
replaceWith = ReplaceWith("beforeVisitChildNodes(node, autoCorrect, emit)"),
)
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: EmitType,
) = rule.visit(node, autoCorrect, emit)

companion object {
private fun createVisitorModifiers(
rule: DiktatRule,
prevRule: KtLintRuleWrapper?,
): Set<VisitorModifier> = prevRule?.id?.qualifiedWithRuleSetId(DIKTAT_RULE_SET_ID)
?.let { previousRuleId ->
val ruleId = rule.id.qualifiedWithRuleSetId(DIKTAT_RULE_SET_ID)
require(ruleId != previousRuleId) {
"PrevRule has same ID as rule: $ruleId"
}
setOf(
VisitorModifier.RunAfterRule(
ruleId = previousRuleId,
loadOnlyWhenOtherRuleIsLoaded = false,
runOnlyWhenOtherRuleIsEnabled = false
)
)
} ?: emptySet()

/**
* @return a rule to which a logic is delegated
*/
internal fun Rule.delegatee(): DiktatRule = (this as? KtLintRuleWrapper)?.rule ?: error("Provided rule ${javaClass.simpleName} is not wrapped by diktat")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.cqfn.diktat.ruleset.rules

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.isRuleEnabled
import org.cqfn.diktat.common.config.rules.qualifiedWithRuleSetId
import org.cqfn.diktat.common.utils.loggerWithKtlintConfig
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.utils.getFilePath
Expand All @@ -16,17 +15,16 @@ private typealias DiktatConfigRule = org.cqfn.diktat.common.config.rules.Rule
/**
* This is a wrapper around _KtLint_ `Rule`.
*
* @param id id of the rule
* @property id id of the rule
* @property configRules all rules from configuration
* @property inspections warnings that are used in the rule's code
*/
@Suppress("TooGenericExceptionCaught")
abstract class DiktatRule(
id: String,
val id: String,
val configRules: List<RulesConfig>,
private val inspections: List<DiktatConfigRule>,
visitorModifiers: Set<VisitorModifier> = emptySet(),
) : Rule(id.qualifiedWithRuleSetId(), visitorModifiers) {
) {
/**
* Default value is false
*/
Expand All @@ -47,8 +45,14 @@ abstract class DiktatRule(
*/
lateinit var emitWarn: EmitType

/**
* @param node
* @param autoCorrect
* @param emit
* @throws Error
*/
@Suppress("TooGenericExceptionThrown")
final override fun visit(
fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: EmitType
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.cqfn.diktat.ruleset.rules

/**
* A group of [DiktatRule]'s as a single set.
*
* @property rules diktat rules.
*/
data class DiktatRuleSet(
val rules: List<DiktatRule>
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.RulesConfigReader
import org.cqfn.diktat.common.utils.loggerWithKtlintConfig
import org.cqfn.diktat.ruleset.constants.Warnings
import org.cqfn.diktat.ruleset.rules.OrderedRuleSet.Companion.ordered
import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming
import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming
import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming
Expand Down Expand Up @@ -87,22 +86,24 @@ 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.Rule

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
* _KtLint_-agnostic factory which creates a [DiktatRuleSet].
*
* @param diktatConfigFile - configuration file where all configurations for inspections and rules are stored
* 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) : RuleSetProvider {
@Suppress("ForbiddenComment")
class DiktatRuleSetProvider(private val diktatConfigFile: String = DIKTAT_ANALYSIS_CONF) {
private val possibleConfigs: Sequence<String?> = sequence {
yield(resolveDefaultConfig())
yield(resolveConfigFileFromJarLocation())
Expand Down Expand Up @@ -134,18 +135,35 @@ class DiktatRuleSetProvider(private val diktatConfigFile: String = DIKTAT_ANALYS
?: 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).
*
* TODO: comments for 0.47.x
* For each invocation of [com.pinterest.ktlint.core.KtLintRuleEngine.lint] and [com.pinterest.ktlint.core.KtLintRuleEngine.format] the [DiktatRuleSet]
* is retrieved.
* This results in new instances of each [Rule] for each file being
* processed.
* As of that a [Rule] does not need to be thread-safe.
*
* However, [com.pinterest.ktlint.core.KtLintRuleEngine.format] requires the [Rule] to be executed twice on a
* file in case at least one violation has been autocorrected.
* As the same [Rule] instance is reused for the second execution of the
* [Rule], the state of the [Rule] is shared.
* As of this [Rule] have to clear their internal state.
*
* @return a default [DiktatRuleSet]
*/
@Suppress(
"LongMethod",
"TOO_LONG_FUNCTION",
)
@Deprecated(
"Marked for removal in KtLint 0.48. See changelog or KDoc for more information.",
)
override fun get(): RuleSet {
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 = listOf(
val rules = sequenceOf(
// comments & documentation
::CommentsRule,
::SingleConstructorRule, // this rule can add properties to a primary constructor, so should be before KdocComments
Expand Down Expand Up @@ -228,12 +246,10 @@ class DiktatRuleSetProvider(private val diktatConfigFile: String = DIKTAT_ANALYS

)
.map {
it.invoke(configRules)
it(configRules)
}
return RuleSet(
DIKTAT_RULE_SET_ID,
rules = rules.toTypedArray()
).ordered()
.toList()
return DiktatRuleSet(rules)
}

private fun validate(config: RulesConfig) =
Expand All @@ -248,8 +264,7 @@ class DiktatRuleSetProvider(private val diktatConfigFile: String = DIKTAT_ANALYS
// for some aggregators of static analyzers we need to provide configuration for cli
// in this case diktat would take the configuration from the directory where jar file is stored
val ruleSetProviderPath =
DiktatRuleSetProvider::class
.java
javaClass
.protectionDomain
.codeSource
.location
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.cqfn.diktat.ruleset.rules

import org.cqfn.diktat.ktlint.KtLintRuleSetProviderWrapper.Companion.toKtLint
import com.pinterest.ktlint.core.RuleSet
import com.pinterest.ktlint.core.RuleSetProvider

/**
* [RuleSetProvider] that provides diKTat ruleset.
*
* By default, it is expected to have `diktat-analysis.yml` configuration in the root folder where 'ktlint' is run
* otherwise it will use default configuration where some rules are disabled.
*
* This class is registered in [resources/META-INF/services/com.pinterest.ktlint.core.RuleSetProvider]
*/
class DiktatRuleSetProviderSpi : RuleSetProvider {
override fun get(): RuleSet = DiktatRuleSetProvider().toKtLint().get()
}

This file was deleted.

Loading

0 comments on commit 4ee2417

Please sign in to comment.