Use annotations to configure rules in rules-complexity (#3768)

* Change definition order in ComplexMethod

* Use annotation in ComplexCondition

* Use annotation in ComplexInterface

* Use annotation in LabeledExpression

* Use annotation in LargeClass

* Use annotation in LongMethod

* Use annotation in LongParameterList

* Use annotation in MethodOverloading

* Use annotation in NamedArguments

The issue message had to be changed as the issue is used to
extract the rule id which is used to lookup the threshold from
the config.

* Use annotation in NestedBlockDepth

* Use annotation in TooManyFunctions

* Use annotation in StringLiteralDuplication

* replace removeSurrounding with removePrefix.removeSuffix

* inline default thresholds to improve readability

Co-authored-by: Markus Schwarz <post@markus-schwarz.net>
This commit is contained in:
marschwar
2021-05-22 14:47:05 +02:00
committed by GitHub
parent 436ab072d0
commit 9494780455
22 changed files with 258 additions and 250 deletions

View File

@@ -5,10 +5,12 @@ import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtDoWhileExpression
import org.jetbrains.kotlin.psi.KtExpression
@@ -36,14 +38,11 @@ import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
*
* fun hasCorrectEnding() = return !str.endsWith("foo") && !str.endsWith("bar") && !str.endsWith("_")
* </compliant>
*
* @configuration threshold - the number of conditions which will trigger the rule (default: `4`)
*/
@ActiveByDefault(since = "1.0.0")
class ComplexCondition(
config: Config = Config.empty,
threshold: Int = DEFAULT_CONDITIONS_COUNT
) : ThresholdRule(config, threshold) {
config: Config = Config.empty
) : Rule(config) {
override val issue = Issue(
"ComplexCondition",
@@ -52,6 +51,9 @@ class ComplexCondition(
Debt.TWENTY_MINS
)
@Configuration("the number of conditions which will trigger the rule")
private val threshold: Int by config(defaultValue = 4)
override fun visitIfExpression(expression: KtIfExpression) {
val condition = expression.condition
checkIfComplex(condition)
@@ -108,8 +110,4 @@ class ComplexCondition(
return count
}
companion object {
const val DEFAULT_CONDITIONS_COUNT = 4
}
}

View File

@@ -5,9 +5,11 @@ import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import io.gitlab.arturbosch.detekt.rules.companionObject
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtClass
@@ -25,15 +27,10 @@ import org.jetbrains.kotlin.psi.psiUtil.isPrivate
*
* Large interfaces should be split into smaller interfaces which have a clear responsibility and are easier
* to understand and implement.
*
* @configuration threshold - the amount of definitions in an interface to trigger the rule (default: `10`)
* @configuration includeStaticDeclarations - whether static declarations should be included (default: `false`)
* @configuration includePrivateDeclarations - whether private declarations should be included (default: `false`)
*/
class ComplexInterface(
config: Config = Config.empty,
threshold: Int = DEFAULT_LARGE_INTERFACE_COUNT
) : ThresholdRule(config, threshold) {
) : Rule(config) {
override val issue = Issue(
javaClass.simpleName,
@@ -45,8 +42,14 @@ class ComplexInterface(
Debt.TWENTY_MINS
)
private val includeStaticDeclarations = valueOrDefault(INCLUDE_STATIC_DECLARATIONS, false)
private val includePrivateDeclarations = valueOrDefault(INCLUDE_PRIVATE_DECLARATIONS, false)
@Configuration("the amount of definitions in an interface to trigger the rule")
private val threshold: Int by config(defaultValue = 10)
@Configuration("whether static declarations should be included")
private val includeStaticDeclarations: Boolean by config(defaultValue = false)
@Configuration("whether private declarations should be included")
private val includePrivateDeclarations: Boolean by config(defaultValue = false)
override fun visitClass(klass: KtClass) {
if (klass.isInterface()) {
@@ -84,10 +87,4 @@ class ComplexInterface(
.filter(PsiElement::considerPrivate)
.count(PsiElement::isMember)
}
companion object {
const val INCLUDE_STATIC_DECLARATIONS = "includeStaticDeclarations"
const val INCLUDE_PRIVATE_DECLARATIONS = "includePrivateDeclarations"
const val DEFAULT_LARGE_INTERFACE_COUNT = 10
}
}

View File

@@ -40,8 +40,15 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
@ActiveByDefault(since = "1.0.0")
class ComplexMethod(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"ComplexMethod",
Severity.Maintainability,
"Prefer splitting up complex methods into smaller, easier to understand methods.",
Debt.TWENTY_MINS
)
@Configuration("McCabe's Cyclomatic Complexity (MCC) number for a method.")
private val threshold: Int by config(DEFAULT_THRESHOLD_METHOD_COMPLEXITY)
private val threshold: Int by config(defaultValue = 15)
@Configuration("Ignores a complex method if it only contains a single when expression.")
private val ignoreSingleWhenExpression: Boolean by config(false)
@@ -55,13 +62,6 @@ class ComplexMethod(config: Config = Config.empty) : Rule(config) {
@Configuration("Comma separated list of function names which add complexity.")
private val nestingFunctions: Set<String> by config(DEFAULT_NESTING_FUNCTIONS) { it.toSet() }
override val issue = Issue(
"ComplexMethod",
Severity.Maintainability,
"Prefer splitting up complex methods into smaller, easier to understand methods.",
Debt.TWENTY_MINS
)
override fun visitNamedFunction(function: KtNamedFunction) {
if (ignoreSingleWhenExpression && hasSingleWhenExpression(function.bodyExpression)) {
return
@@ -100,7 +100,6 @@ class ComplexMethod(config: Config = Config.empty) : Rule(config) {
this is KtReturnExpression && this.returnedExpression is KtWhenExpression
companion object {
const val DEFAULT_THRESHOLD_METHOD_COMPLEXITY = 15
val DEFAULT_NESTING_FUNCTIONS = listOf(
"run",
"let",

View File

@@ -7,7 +7,8 @@ import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.internal.valueOrDefaultCommaSeparated
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpressionWithLabel
@@ -57,9 +58,6 @@ import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
* }
* }
* </compliant>
*
* @configuration ignoredLabels - allows to provide a list of label names which should be ignored by this rule
* (default: `[]`)
*/
class LabeledExpression(config: Config = Config.empty) : Rule(config) {
@@ -70,14 +68,16 @@ class LabeledExpression(config: Config = Config.empty) : Rule(config) {
Debt.TWENTY_MINS
)
private val ignoredLabels = valueOrDefaultCommaSeparated(IGNORED_LABELS, emptyList())
.map { it.removePrefix("*").removeSuffix("*") }
@Configuration("allows to provide a list of label names which should be ignored by this rule")
private val ignoredLabels: List<String> by config(listOf<String>()) { list ->
list.map { it.removePrefix("*").removeSuffix("*") }
}
override fun visitExpressionWithLabel(expression: KtExpressionWithLabel) {
super.visitExpressionWithLabel(expression)
if (expression !is KtThisExpression || isNotReferencingOuterClass(expression)) {
expression.getLabelName()?.let { labelName ->
if (!ignoredLabels.any { labelName.contains(it, ignoreCase = true) }) {
if (ignoredLabels.none { labelName.contains(it, ignoreCase = true) }) {
report(CodeSmell(issue, Entity.from(expression), issue.description))
}
}
@@ -104,8 +104,4 @@ class LabeledExpression(config: Config = Config.empty) : Rule(config) {
classes.add(containingClass)
getClassHierarchy(containingClass, classes)
}
companion object {
const val IGNORED_LABELS = "ignoredLabels"
}
}

View File

@@ -6,10 +6,12 @@ import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -21,14 +23,9 @@ import java.util.IdentityHashMap
* the class does instead handle multiple responsibilities. Instead of doing many things at once prefer to
* split up large classes into smaller classes. These smaller classes are then easier to understand and handle less
* things.
*
* @configuration threshold - the size of class required to trigger the rule (default: `600`)
*/
@ActiveByDefault(since = "1.0.0")
class LargeClass(
config: Config = Config.empty,
threshold: Int = DEFAULT_THRESHOLD_CLASS_LENGTH
) : ThresholdRule(config, threshold) {
class LargeClass(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"LargeClass",
@@ -38,6 +35,9 @@ class LargeClass(
Debt.TWENTY_MINS
)
@Configuration("the size of class required to trigger the rule")
private val threshold: Int by config(defaultValue = 600)
private val classToLinesCache = IdentityHashMap<KtClassOrObject, Int>()
private val nestedClassTracking = IdentityHashMap<KtClassOrObject, HashSet<KtClassOrObject>>()
@@ -80,8 +80,4 @@ class LargeClass(
nestedClasses = nestedClasses.mapNotNull { nestedClassTracking[it] }.flattenTo(HashSet())
}
}
companion object {
const val DEFAULT_THRESHOLD_CLASS_LENGTH = 600
}
}

View File

@@ -6,10 +6,12 @@ import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -21,14 +23,9 @@ import java.util.IdentityHashMap
* Prefer smaller methods with clear names that describe their functionality clearly.
*
* Extract parts of the functionality of long methods into separate, smaller methods.
*
* @configuration threshold - number of lines in a method to trigger the rule (default: `60`)
*/
@ActiveByDefault(since = "1.0.0")
class LongMethod(
config: Config = Config.empty,
threshold: Int = DEFAULT_THRESHOLD_METHOD_LENGTH
) : ThresholdRule(config, threshold) {
class LongMethod(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"LongMethod",
@@ -38,6 +35,9 @@ class LongMethod(
Debt.TWENTY_MINS
)
@Configuration("number of lines in a method to trigger the rule")
private val threshold: Int by config(defaultValue = 60)
private val functionToLinesCache = HashMap<KtNamedFunction, Int>()
private val functionToBodyLinesCache = HashMap<KtNamedFunction, Int>()
private val nestedFunctionTracking = IdentityHashMap<KtNamedFunction, HashSet<KtNamedFunction>>()
@@ -91,8 +91,4 @@ class LongMethod(
nestedFunctions = nestedFunctions.mapNotNull { nestedFunctionTracking[it] }.flattenTo(HashSet())
}
}
companion object {
const val DEFAULT_THRESHOLD_METHOD_LENGTH = 60
}
}

View File

@@ -10,7 +10,9 @@ import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.valueOrDefaultCommaSeparated
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import io.gitlab.arturbosch.detekt.api.internal.configWithFallback
import io.gitlab.arturbosch.detekt.rules.isOverride
import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtClass
@@ -25,22 +27,9 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
/**
* Reports functions and constructors which have more parameters than a certain threshold.
*
* @configuration threshold - number of parameters required to trigger the rule (default: `6`)
* (deprecated: "Use `functionThreshold` and `constructorThreshold` instead")
* @configuration functionThreshold - number of function parameters required to trigger the rule (default: `6`)
* @configuration constructorThreshold - number of constructor parameters required to trigger the rule (default: `7`)
* @configuration ignoreDefaultParameters - ignore parameters that have a default value (default: `false`)
* @configuration ignoreDataClasses - ignore long constructor parameters list for data classes (default: `true`)
* @configuration ignoreAnnotated - ignore long parameters list for constructors or functions in the context of these
* annotation class names (default: `[]`); (e.g. ['Inject', 'Module', 'Suppress']);
* the most common case is for dependency injection where constructors are annotated with @Inject.
*/
@ActiveByDefault(since = "1.0.0")
class LongParameterList(
config: Config = Config.empty
) : Rule(config) {
class LongParameterList(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"LongParameterList",
Severity.Maintainability,
@@ -50,18 +39,37 @@ class LongParameterList(
Debt.TWENTY_MINS
)
private val functionThreshold: Int =
valueOrDefault(FUNCTION_THRESHOLD, valueOrDefault(THRESHOLD, DEFAULT_FUNCTION_THRESHOLD))
@Suppress("unused")
@Deprecated("Use `functionThreshold` and `constructorThreshold` instead")
@Configuration("number of parameters required to trigger the rule")
private val threshold: Int by config(DEFAULT_FUNCTION_THRESHOLD)
private val constructorThreshold: Int =
valueOrDefault(CONSTRUCTOR_THRESHOLD, valueOrDefault(THRESHOLD, DEFAULT_CONSTRUCTOR_THRESHOLD))
@Configuration("number of function parameters required to trigger the rule")
private val functionThreshold: Int by configWithFallback(
fallbackPropertyName = "threshold",
defaultValue = DEFAULT_FUNCTION_THRESHOLD
)
private val ignoreDefaultParameters = valueOrDefault(IGNORE_DEFAULT_PARAMETERS, false)
@Configuration("number of constructor parameters required to trigger the rule")
private val constructorThreshold: Int by configWithFallback(
fallbackPropertyName = "threshold",
defaultValue = DEFAULT_CONSTRUCTOR_THRESHOLD
)
private val ignoreDataClasses = valueOrDefault(IGNORE_DATA_CLASSES, true)
@Configuration("ignore parameters that have a default value")
private val ignoreDefaultParameters: Boolean by config(defaultValue = false)
private val ignoreAnnotated = valueOrDefaultCommaSeparated(IGNORE_ANNOTATED, emptyList())
.map { it.removePrefix("*").removeSuffix("*") }
@Configuration("ignore long constructor parameters list for data classes")
private val ignoreDataClasses: Boolean by config(defaultValue = true)
@Configuration(
"ignore long parameters list for constructors or functions in the " +
"context of these annotation class names; (e.g. ['Inject', 'Module', 'Suppress']); " +
"the most common case is for dependency injection where constructors are annotated with `@Inject`."
)
private val ignoreAnnotated: List<String> by config(listOf<String>()) { list ->
list.map { it.removePrefix("*").removeSuffix("*") }
}
private lateinit var annotationExcluder: AnnotationExcluder
@@ -131,14 +139,7 @@ class LongParameterList(
}
companion object {
const val THRESHOLD = "threshold"
const val FUNCTION_THRESHOLD = "functionThreshold"
const val CONSTRUCTOR_THRESHOLD = "constructorThreshold"
const val IGNORE_DEFAULT_PARAMETERS = "ignoreDefaultParameters"
const val IGNORE_DATA_CLASSES = "ignoreDataClasses"
const val IGNORE_ANNOTATED = "ignoreAnnotated"
const val DEFAULT_FUNCTION_THRESHOLD = 6
const val DEFAULT_CONSTRUCTOR_THRESHOLD = 7
private const val DEFAULT_FUNCTION_THRESHOLD = 6
private const val DEFAULT_CONSTRUCTOR_THRESHOLD = 7
}
}

View File

@@ -6,9 +6,11 @@ import io.gitlab.arturbosch.detekt.api.DetektVisitor
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import io.gitlab.arturbosch.detekt.rules.isOverride
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtEnumEntry
@@ -23,13 +25,8 @@ import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
* Method overloading tightly couples these methods together which might make the code harder to understand.
*
* Refactor these methods and try to use optional parameters instead to prevent some of the overloading.
*
* @configuration threshold - number of overloads which will trigger the rule (default: `6`)
*/
class MethodOverloading(
config: Config = Config.empty,
threshold: Int = DEFAULT_THRESHOLD_OVERLOAD_COUNT
) : ThresholdRule(config, threshold) {
class MethodOverloading(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"MethodOverloading",
@@ -40,6 +37,9 @@ class MethodOverloading(
Debt.TWENTY_MINS
)
@Configuration("number of overloads which will trigger the rule")
private val threshold: Int by config(defaultValue = 6)
override fun visitKtFile(file: KtFile) {
val visitor = OverloadedMethodVisitor()
file.getChildrenOfType<KtNamedFunction>().forEach { visitor.visitMethod(it) }
@@ -93,8 +93,4 @@ class MethodOverloading(
private fun KtNamedFunction.isOverriddenInsideEnumEntry() = containingClass() is KtEnumEntry && isOverride()
}
companion object {
const val DEFAULT_THRESHOLD_OVERLOAD_COUNT = 6
}
}

View File

@@ -5,9 +5,11 @@ import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import io.gitlab.arturbosch.detekt.api.internal.config
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtLambdaArgument
import org.jetbrains.kotlin.resolve.BindingContext
@@ -28,22 +30,20 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
* }
* sum(a = 1, b = 2, c = 3, d = 4)
* </compliant>
*
* @configuration threshold - number of parameters that triggers this inspection (default: `3`)
*/
@RequiresTypeResolution
class NamedArguments(
config: Config = Config.empty,
threshold: Int = DEFAULT_FUNCTION_THRESHOLD
) : ThresholdRule(config, threshold) {
class NamedArguments(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"NamedArguments",
Severity.Maintainability,
"Function invocation with more than $threshold parameters must all be named",
"Parameters of function invocation must all be named",
Debt.FIVE_MINS
)
@Configuration("number of parameters that triggers this inspection")
private val threshold: Int by config(defaultValue = 3)
override fun visitCallExpression(expression: KtCallExpression) {
if (bindingContext == BindingContext.EMPTY) return
val valueArguments = expression.valueArguments
@@ -64,8 +64,4 @@ class NamedArguments(
resolvedCall.getParameterForArgument(it)?.varargElementType == null || it.getSpreadElement() != null
}
}
companion object {
const val DEFAULT_FUNCTION_THRESHOLD = 3
}
}

View File

@@ -6,10 +6,12 @@ import io.gitlab.arturbosch.detekt.api.DetektVisitor
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import io.gitlab.arturbosch.detekt.rules.isUsedForNesting
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody
@@ -25,14 +27,9 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
* its hidden complexity. It might become harder to understand edge-cases of the function.
*
* Prefer extracting the nested code into well-named functions to make it easier to understand.
*
* @configuration threshold - the nested depth required to trigger rule (default: `4`)
*/
@ActiveByDefault(since = "1.0.0")
class NestedBlockDepth(
config: Config = Config.empty,
threshold: Int = DEFAULT_THRESHOLD_NESTING
) : ThresholdRule(config, threshold) {
class NestedBlockDepth(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
"NestedBlockDepth",
@@ -42,6 +39,9 @@ class NestedBlockDepth(
Debt.TWENTY_MINS
)
@Configuration("the nested depth required to trigger rule")
private val threshold: Int by config(defaultValue = 4)
override fun visitNamedFunction(function: KtNamedFunction) {
val visitor = FunctionDepthVisitor(threshold)
visitor.visitNamedFunction(function)
@@ -122,8 +122,4 @@ class NestedBlockDepth(
}
}
}
companion object {
const val DEFAULT_THRESHOLD_NESTING = 4
}
}

View File

@@ -5,11 +5,12 @@ import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.DetektVisitor
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.LazyRegex
import io.gitlab.arturbosch.detekt.api.Metric
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdRule
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import io.gitlab.arturbosch.detekt.rules.isPartOf
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
@@ -41,16 +42,8 @@ import org.jetbrains.kotlin.psi.psiUtil.plainContent
* }
* }
* </compliant>
*
* @configuration threshold - amount of duplications to trigger rule (default: `3`)
* @configuration ignoreAnnotation - if values in Annotations should be ignored (default: `true`)
* @configuration excludeStringsWithLessThan5Characters - if short strings should be excluded (default: `true`)
* @configuration ignoreStringsRegex - RegEx of Strings that should be ignored (default: `'$^'`)
*/
class StringLiteralDuplication(
config: Config = Config.empty,
threshold: Int = DEFAULT_DUPLICATION
) : ThresholdRule(config, threshold) {
class StringLiteralDuplication(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
javaClass.simpleName,
@@ -59,9 +52,17 @@ class StringLiteralDuplication(
Debt.FIVE_MINS
)
private val ignoreAnnotation = valueOrDefault(IGNORE_ANNOTATION, true)
private val excludeStringsWithLessThan5Characters = valueOrDefault(EXCLUDE_SHORT_STRING, true)
private val ignoreStringsRegex by LazyRegex(IGNORE_STRINGS_REGEX, "$^")
@Configuration("amount of duplications to trigger rule")
private val threshold: Int by config(defaultValue = 3)
@Configuration("if values in Annotations should be ignored")
private val ignoreAnnotation: Boolean by config(true)
@Configuration("if short strings should be excluded")
private val excludeStringsWithLessThan5Characters: Boolean by config(true)
@Configuration("RegEx of Strings that should be ignored")
private val ignoreStringsRegex: Regex by config("$^", String::toRegex)
override fun visitKtFile(file: KtFile) {
val visitor = StringLiteralVisitor()
@@ -116,10 +117,6 @@ class StringLiteralDuplication(
}
companion object {
const val DEFAULT_DUPLICATION = 3
const val STRING_EXCLUSION_LENGTH = 5
const val IGNORE_ANNOTATION = "ignoreAnnotation"
const val EXCLUDE_SHORT_STRING = "excludeStringsWithLessThan5Characters"
const val IGNORE_STRINGS_REGEX = "ignoreStringsRegex"
private const val STRING_EXCLUSION_LENGTH = 5
}
}

View File

@@ -9,6 +9,8 @@ import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.config
import io.gitlab.arturbosch.detekt.rules.hasAnnotation
import io.gitlab.arturbosch.detekt.rules.isOverride
import org.jetbrains.kotlin.psi.KtClass
@@ -24,15 +26,6 @@ import org.jetbrains.kotlin.psi.psiUtil.isPrivate
*
* Too many functions indicate a violation of the single responsibility principle. Prefer extracting functionality
* which clearly belongs together in separate parts of the code.
*
* @configuration thresholdInFiles - threshold in files (default: `11`)
* @configuration thresholdInClasses - threshold in classes (default: `11`)
* @configuration thresholdInInterfaces - threshold in interfaces (default: `11`)
* @configuration thresholdInObjects - threshold in objects (default: `11`)
* @configuration thresholdInEnums - threshold in enums (default: `11`)
* @configuration ignoreDeprecated - ignore deprecated functions (default: `false`)
* @configuration ignorePrivate - ignore private functions (default: `false`)
* @configuration ignoreOverridden - ignore overridden functions (default: `false`)
*/
@ActiveByDefault(since = "1.0.0")
class TooManyFunctions(config: Config = Config.empty) : Rule(config) {
@@ -46,14 +39,29 @@ class TooManyFunctions(config: Config = Config.empty) : Rule(config) {
Debt.TWENTY_MINS
)
private val thresholdInFiles = valueOrDefault(THRESHOLD_IN_FILES, DEFAULT_THRESHOLD)
private val thresholdInClasses = valueOrDefault(THRESHOLD_IN_CLASSES, DEFAULT_THRESHOLD)
private val thresholdInObjects = valueOrDefault(THRESHOLD_IN_OBJECTS, DEFAULT_THRESHOLD)
private val thresholdInInterfaces = valueOrDefault(THRESHOLD_IN_INTERFACES, DEFAULT_THRESHOLD)
private val thresholdInEnums = valueOrDefault(THRESHOLD_IN_ENUMS, DEFAULT_THRESHOLD)
private val ignoreDeprecated = valueOrDefault(IGNORE_DEPRECATED, false)
private val ignorePrivate = valueOrDefault(IGNORE_PRIVATE, false)
private val ignoreOverridden = valueOrDefault(IGNORE_OVERRIDDEN, false)
@Configuration("threshold in files")
private val thresholdInFiles: Int by config(DEFAULT_THRESHOLD)
@Configuration("threshold in classes")
private val thresholdInClasses: Int by config(DEFAULT_THRESHOLD)
@Configuration("threshold in interfaces")
private val thresholdInInterfaces: Int by config(DEFAULT_THRESHOLD)
@Configuration("threshold in objects")
private val thresholdInObjects: Int by config(DEFAULT_THRESHOLD)
@Configuration("threshold in enums")
private val thresholdInEnums: Int by config(DEFAULT_THRESHOLD)
@Configuration("ignore deprecated functions")
private val ignoreDeprecated: Boolean by config(false)
@Configuration("ignore private functions")
private val ignorePrivate: Boolean by config(false)
@Configuration("ignore overridden functions")
private val ignoreOverridden: Boolean by config(false)
private var amountOfTopLevelFunctions: Int = 0
@@ -160,14 +168,6 @@ class TooManyFunctions(config: Config = Config.empty) : Rule(config) {
companion object {
const val DEFAULT_THRESHOLD = 11
const val THRESHOLD_IN_FILES = "thresholdInFiles"
const val THRESHOLD_IN_CLASSES = "thresholdInClasses"
const val THRESHOLD_IN_INTERFACES = "thresholdInInterfaces"
const val THRESHOLD_IN_OBJECTS = "thresholdInObjects"
const val THRESHOLD_IN_ENUMS = "thresholdInEnums"
const val IGNORE_DEPRECATED = "ignoreDeprecated"
const val IGNORE_PRIVATE = "ignorePrivate"
const val IGNORE_OVERRIDDEN = "ignoreOverridden"
private const val DEPRECATED = "Deprecated"
}
}

View File

@@ -6,11 +6,18 @@ import org.assertj.core.api.Assertions.assertThat
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
private const val THRESHOLD = 4
private val defaultConfigMap = mapOf("threshold" to THRESHOLD)
private val staticDeclarationsConfig = TestConfig(
defaultConfigMap + ("includeStaticDeclarations" to true)
)
private val privateDeclarationsConfig = TestConfig(
defaultConfigMap + ("includePrivateDeclarations" to true)
)
class ComplexInterfaceSpec : Spek({
val subject by memoized { ComplexInterface(threshold = THRESHOLD) }
val staticDeclarationsConfig by memoized { TestConfig(mapOf(ComplexInterface.INCLUDE_STATIC_DECLARATIONS to "true")) }
val privateDeclarationsConfig by memoized { TestConfig(mapOf(ComplexInterface.INCLUDE_PRIVATE_DECLARATIONS to "true")) }
val subject by memoized { ComplexInterface(TestConfig(defaultConfigMap)) }
describe("ComplexInterface rule positives") {
@@ -29,7 +36,7 @@ class ComplexInterfaceSpec : Spek({
}
it("reports complex interface with includeStaticDeclarations config") {
val rule = ComplexInterface(staticDeclarationsConfig, threshold = THRESHOLD)
val rule = ComplexInterface(staticDeclarationsConfig)
assertThat(rule.compileAndLint(code)).hasSize(1)
}
}
@@ -51,7 +58,7 @@ class ComplexInterfaceSpec : Spek({
}
it("reports complex interface with includeStaticDeclarations config") {
val rule = ComplexInterface(staticDeclarationsConfig, threshold = THRESHOLD)
val rule = ComplexInterface(staticDeclarationsConfig)
assertThat(rule.compileAndLint(code)).hasSize(1)
}
}
@@ -73,7 +80,7 @@ class ComplexInterfaceSpec : Spek({
}
it("reports complex interface with includeStaticDeclarations config") {
val rule = ComplexInterface(staticDeclarationsConfig, threshold = THRESHOLD)
val rule = ComplexInterface(staticDeclarationsConfig)
assertThat(rule.compileAndLint(code)).hasSize(1)
}
}
@@ -93,7 +100,7 @@ class ComplexInterfaceSpec : Spek({
}
it("does report complex interface with includePrivateDeclarations config") {
val rule = ComplexInterface(privateDeclarationsConfig, threshold = THRESHOLD)
val rule = ComplexInterface(privateDeclarationsConfig)
assertThat(rule.compileAndLint(code)).hasSize(1)
}
}
@@ -114,7 +121,7 @@ class ComplexInterfaceSpec : Spek({
}
it("does report complex interface with includePrivateDeclarations config") {
val rule = ComplexInterface(privateDeclarationsConfig, threshold = THRESHOLD)
val rule = ComplexInterface(privateDeclarationsConfig)
assertThat(rule.compileAndLint(code)).hasSize(1)
}
}
@@ -157,5 +164,3 @@ class ComplexInterfaceSpec : Spek({
}
}
})
private const val THRESHOLD = 4

View File

@@ -134,7 +134,7 @@ class LabeledExpressionSpec : Spek({
loop@ for (i in 1..5) {}
}
"""
val config = TestConfig(mapOf(LabeledExpression.IGNORED_LABELS to listOf("loop")))
val config = TestConfig(mapOf("ignoredLabels" to listOf("loop")))
val findings = LabeledExpression(config).compileAndLint(code)
assertThat(findings).isEmpty()
}
@@ -145,7 +145,18 @@ class LabeledExpressionSpec : Spek({
loop@ for (i in 1..5) {}
}
"""
val config = TestConfig(mapOf(LabeledExpression.IGNORED_LABELS to "loop"))
val config = TestConfig(mapOf("ignoredLabels" to "loop"))
val findings = LabeledExpression(config).compileAndLint(code)
assertThat(findings).isEmpty()
}
it("does not report excluded label config with leading and trailing wildcard") {
val code = """
fun f() {
loop@ for (i in 1..5) {}
}
"""
val config = TestConfig(mapOf("ignoredLabels" to "*loop*,other"))
val findings = LabeledExpression(config).compileAndLint(code)
assertThat(findings).isEmpty()
}

View File

@@ -2,18 +2,21 @@ package io.gitlab.arturbosch.detekt.rules.complexity
import io.github.detekt.test.utils.resourceAsPath
import io.gitlab.arturbosch.detekt.api.SourceLocation
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.assertThat
import io.gitlab.arturbosch.detekt.test.compileAndLint
import io.gitlab.arturbosch.detekt.test.lint
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
private fun subject(threshold: Int) = LargeClass(TestConfig(mapOf("threshold" to threshold)))
class LargeClassSpec : Spek({
describe("nested classes are also considered") {
it("should detect only the nested large class which exceeds threshold 70") {
val findings = LargeClass(threshold = 70).lint(resourceAsPath("NestedClasses.kt"))
val findings = subject(threshold = 70).lint(resourceAsPath("NestedClasses.kt"))
assertThat(findings).hasSize(1)
assertThat(findings).hasSourceLocations(SourceLocation(12, 15))
}
@@ -30,7 +33,7 @@ class LargeClassSpec : Spek({
println()
}
"""
val rule = LargeClass(threshold = 2)
val rule = subject(threshold = 2)
assertThat(rule.compileAndLint(code)).isEmpty()
}
}

View File

@@ -1,6 +1,7 @@
package io.gitlab.arturbosch.detekt.rules.complexity
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.assertThat
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.spekframework.spek2.Spek
@@ -8,7 +9,7 @@ import org.spekframework.spek2.style.specification.describe
class LongMethodSpec : Spek({
val subject by memoized { LongMethod(threshold = 5) }
val subject by memoized { LongMethod(TestConfig(mapOf("threshold" to 5))) }
describe("nested functions can be long") {

View File

@@ -12,8 +12,8 @@ class LongParameterListSpec : Spek({
val defaultConfig by memoized {
TestConfig(
mapOf(
LongParameterList.FUNCTION_THRESHOLD to defaultThreshold,
LongParameterList.CONSTRUCTOR_THRESHOLD to defaultThreshold
"functionThreshold" to defaultThreshold,
"constructorThreshold" to defaultThreshold
)
)
}
@@ -45,7 +45,7 @@ class LongParameterListSpec : Spek({
}
it("does not report long parameter list if parameters with defaults should be ignored") {
val config = TestConfig(mapOf(LongParameterList.IGNORE_DEFAULT_PARAMETERS to "true"))
val config = TestConfig(mapOf("ignoreDefaultParameters" to "true"))
val rule = LongParameterList(config)
val code = "fun long(a: Int, b: Int, c: Int = 2) {}"
assertThat(rule.compileAndLint(code)).isEmpty()
@@ -76,7 +76,7 @@ class LongParameterListSpec : Spek({
}
it("reports long parameter list if custom threshold is set") {
val config = TestConfig(mapOf(LongParameterList.CONSTRUCTOR_THRESHOLD to "1"))
val config = TestConfig(mapOf("constructorThreshold" to "1"))
val rule = LongParameterList(config)
val code = "class LongCtor(a: Int)"
assertThat(rule.compileAndLint(code)).hasSize(1)
@@ -85,8 +85,8 @@ class LongParameterListSpec : Spek({
it("does not report long parameter list for constructors of data classes if asked") {
val config = TestConfig(
mapOf(
LongParameterList.IGNORE_DATA_CLASSES to "true",
LongParameterList.CONSTRUCTOR_THRESHOLD to "1"
"ignoreDataClasses" to "true",
"constructorThreshold" to "1"
)
)
val rule = LongParameterList(config)
@@ -99,9 +99,13 @@ class LongParameterListSpec : Spek({
val config by memoized {
TestConfig(
mapOf(
LongParameterList.IGNORE_ANNOTATED to listOf("Generated", "kotlin.Deprecated", "kotlin.jvm.JvmName"),
LongParameterList.FUNCTION_THRESHOLD to 1,
LongParameterList.CONSTRUCTOR_THRESHOLD to 1
"ignoreAnnotated" to listOf(
"Generated",
"kotlin.Deprecated",
"kotlin.jvm.JvmName"
),
"functionThreshold" to 1,
"constructorThreshold" to 1
)
)
}

View File

@@ -1,13 +1,16 @@
package io.gitlab.arturbosch.detekt.rules.complexity
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.assertj.core.api.Assertions.assertThat
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
class MethodOverloadingSpec : Spek({
val defaultThreshold = 3
val defaultConfig by memoized { TestConfig(mapOf("threshold" to defaultThreshold)) }
val subject by memoized { MethodOverloading(threshold = 3) }
val subject by memoized { MethodOverloading(defaultConfig) }
describe("MethodOverloading rule") {

View File

@@ -1,6 +1,7 @@
package io.gitlab.arturbosch.detekt.rules.complexity
import io.gitlab.arturbosch.detekt.rules.setupKotlinEnvironment
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
@@ -12,11 +13,11 @@ class NamedArgumentsSpec : Spek({
val env: KotlinCoreEnvironment by memoized()
val defaultThreshold = 2
val namedArguments by memoized { NamedArguments(threshold = defaultThreshold) }
val defaultConfig by memoized { TestConfig(mapOf("threshold" to defaultThreshold)) }
val subject by memoized { NamedArguments(defaultConfig) }
describe("NameArguments rule") {
val errorMessage = "Function invocation with more than $defaultThreshold parameters must all be named"
it("invocation with more than 2 parameters should throw error") {
val code = """
fun sum(a: Int, b:Int, c:Int) {
@@ -26,9 +27,8 @@ class NamedArgumentsSpec : Spek({
sum(1, 2, 3)
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
assertThat(findings.first().message).isEqualTo(errorMessage)
}
it("Function invocation with more than 2 parameters should not throw error if named") {
@@ -40,7 +40,7 @@ class NamedArgumentsSpec : Spek({
sum(a = 1, b = 2, c = 3)
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -53,9 +53,8 @@ class NamedArgumentsSpec : Spek({
sum(1, b = 2, c = 3)
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
assertThat(findings.first().message).isEqualTo(errorMessage)
}
it("invocation with less than 3 parameters should not throw error") {
@@ -67,7 +66,7 @@ class NamedArgumentsSpec : Spek({
sum(1, 2)
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -80,7 +79,7 @@ class NamedArgumentsSpec : Spek({
sum(a = 1, b = 2)
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -90,9 +89,8 @@ class NamedArgumentsSpec : Spek({
val obj = C(1, 2, 3)
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
assertThat(findings.first().message).isEqualTo(errorMessage)
}
it("constructor invocation with more than 3 named parameters should not throw error") {
@@ -101,7 +99,7 @@ class NamedArgumentsSpec : Spek({
val obj = C(a = 1, b = 2, c= 3)
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -111,7 +109,7 @@ class NamedArgumentsSpec : Spek({
val obj = C(1, 2)
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -123,7 +121,7 @@ class NamedArgumentsSpec : Spek({
LocalDateTime.of(2020, 3, 13, 14, 0, 0)
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -136,7 +134,7 @@ class NamedArgumentsSpec : Spek({
bar(1, 2, 3, "a")
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -147,7 +145,7 @@ class NamedArgumentsSpec : Spek({
bar(1, 2, 3, *arrayOf("a"))
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
@@ -160,7 +158,7 @@ class NamedArgumentsSpec : Spek({
foo(a = 1, b = 2, c = 3, { it })
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
@@ -172,7 +170,7 @@ class NamedArgumentsSpec : Spek({
foo(a = 1, b = 2, c = 3) { it }
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}
@@ -184,7 +182,7 @@ class NamedArgumentsSpec : Spek({
foo(a = 1, b = 2, 3) { it }
}
"""
val findings = namedArguments.compileAndLintWithContext(env, code)
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
}

View File

@@ -2,6 +2,7 @@ package io.gitlab.arturbosch.detekt.rules.complexity
import io.github.detekt.test.utils.resourceAsPath
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.assertThat
import io.gitlab.arturbosch.detekt.test.compileAndLint
import io.gitlab.arturbosch.detekt.test.lint
@@ -11,7 +12,9 @@ import org.spekframework.spek2.style.specification.describe
class NestedBlockDepthSpec : Spek({
val subject by memoized { NestedBlockDepth(threshold = 4) }
val defaultThreshold = 4
val defaultConfig by memoized { TestConfig(mapOf("threshold" to defaultThreshold)) }
val subject by memoized { NestedBlockDepth(defaultConfig) }
describe("nested classes are also considered") {
it("should detect only the nested large class") {

View File

@@ -8,6 +8,10 @@ import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
import java.util.regex.PatternSyntaxException
private const val IGNORE_ANNOTATION = "ignoreAnnotation"
private const val EXCLUDE_SHORT_STRING = "excludeStringsWithLessThan5Characters"
private const val IGNORE_STRINGS_REGEX = "ignoreStringsRegex"
class StringLiteralDuplicationSpec : Spek({
val subject by memoized { StringLiteralDuplication() }
@@ -49,7 +53,7 @@ class StringLiteralDuplicationSpec : Spek({
}
it("reports strings in annotations according to config") {
val config = TestConfig(mapOf(StringLiteralDuplication.IGNORE_ANNOTATION to "false"))
val config = TestConfig(mapOf(IGNORE_ANNOTATION to "false"))
assertFindingWithConfig(code, config, 1)
}
}
@@ -63,7 +67,7 @@ class StringLiteralDuplicationSpec : Spek({
}
it("reports string with 4 characters") {
val config = TestConfig(mapOf(StringLiteralDuplication.EXCLUDE_SHORT_STRING to "false"))
val config = TestConfig(mapOf(EXCLUDE_SHORT_STRING to "false"))
assertFindingWithConfig(code, config, 1)
}
}
@@ -80,21 +84,21 @@ class StringLiteralDuplicationSpec : Spek({
val str1 = "lorem" + "lorem" + "lorem"
val str2 = "ipsum" + "ipsum" + "ipsum"
"""
val config = TestConfig(mapOf(StringLiteralDuplication.IGNORE_STRINGS_REGEX to "(lorem|ipsum)"))
val config = TestConfig(mapOf(IGNORE_STRINGS_REGEX to "(lorem|ipsum)"))
assertFindingWithConfig(code, config, 0)
}
it("should not fail with invalid regex when disabled") {
val configValues = mapOf(
"active" to "false",
StringLiteralDuplication.IGNORE_STRINGS_REGEX to "*lorem"
IGNORE_STRINGS_REGEX to "*lorem"
)
val config = TestConfig(configValues)
assertFindingWithConfig(regexTestingCode, config, 0)
}
it("should fail with invalid regex") {
val config = TestConfig(mapOf(StringLiteralDuplication.IGNORE_STRINGS_REGEX to "*lorem"))
val config = TestConfig(mapOf(IGNORE_STRINGS_REGEX to "*lorem"))
assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy {
StringLiteralDuplication(config).compileAndLint(regexTestingCode)
}

View File

@@ -6,19 +6,27 @@ import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
class TooManyFunctionsSpec : Spek({
private const val THRESHOLD_IN_FILES = "thresholdInFiles"
private const val THRESHOLD_IN_CLASSES = "thresholdInClasses"
private const val THRESHOLD_IN_INTERFACES = "thresholdInInterfaces"
private const val THRESHOLD_IN_OBJECTS = "thresholdInObjects"
private const val THRESHOLD_IN_ENUMS = "thresholdInEnums"
private const val IGNORE_DEPRECATED = "ignoreDeprecated"
private const val IGNORE_PRIVATE = "ignorePrivate"
private const val IGNORE_OVERRIDDEN = "ignoreOverridden"
object TooManyFunctionsSpec : Spek({
describe("different declarations with one function as threshold") {
val rule by memoized {
TooManyFunctions(
TestConfig(
mapOf(
TooManyFunctions.THRESHOLD_IN_CLASSES to "1",
TooManyFunctions.THRESHOLD_IN_ENUMS to "1",
TooManyFunctions.THRESHOLD_IN_FILES to "1",
TooManyFunctions.THRESHOLD_IN_INTERFACES to "1",
TooManyFunctions.THRESHOLD_IN_OBJECTS to "1"
THRESHOLD_IN_CLASSES to "1",
THRESHOLD_IN_ENUMS to "1",
THRESHOLD_IN_FILES to "1",
THRESHOLD_IN_INTERFACES to "1",
THRESHOLD_IN_OBJECTS to "1"
)
)
)
@@ -128,9 +136,9 @@ class TooManyFunctionsSpec : Spek({
val configuredRule = TooManyFunctions(
TestConfig(
mapOf(
TooManyFunctions.THRESHOLD_IN_CLASSES to "1",
TooManyFunctions.THRESHOLD_IN_FILES to "1",
TooManyFunctions.IGNORE_DEPRECATED to "true"
THRESHOLD_IN_CLASSES to "1",
THRESHOLD_IN_FILES to "1",
IGNORE_DEPRECATED to "true"
)
)
)
@@ -154,9 +162,9 @@ class TooManyFunctionsSpec : Spek({
val configuredRule = TooManyFunctions(
TestConfig(
mapOf(
TooManyFunctions.THRESHOLD_IN_CLASSES to "1",
TooManyFunctions.THRESHOLD_IN_FILES to "1",
TooManyFunctions.IGNORE_PRIVATE to "true"
THRESHOLD_IN_CLASSES to "1",
THRESHOLD_IN_FILES to "1",
IGNORE_PRIVATE to "true"
)
)
)
@@ -188,11 +196,11 @@ class TooManyFunctionsSpec : Spek({
val configuredRule = TooManyFunctions(
TestConfig(
mapOf(
TooManyFunctions.THRESHOLD_IN_CLASSES to "1",
TooManyFunctions.THRESHOLD_IN_FILES to "1",
TooManyFunctions.IGNORE_PRIVATE to "true",
TooManyFunctions.IGNORE_DEPRECATED to "true",
TooManyFunctions.IGNORE_OVERRIDDEN to "true"
THRESHOLD_IN_CLASSES to "1",
THRESHOLD_IN_FILES to "1",
IGNORE_PRIVATE to "true",
IGNORE_DEPRECATED to "true",
IGNORE_OVERRIDDEN to "true"
)
)
)
@@ -218,9 +226,9 @@ class TooManyFunctionsSpec : Spek({
val configuredRule = TooManyFunctions(
TestConfig(
mapOf(
TooManyFunctions.THRESHOLD_IN_CLASSES to "1",
TooManyFunctions.THRESHOLD_IN_FILES to "1",
TooManyFunctions.IGNORE_OVERRIDDEN to "true"
THRESHOLD_IN_CLASSES to "1",
THRESHOLD_IN_FILES to "1",
IGNORE_OVERRIDDEN to "true"
)
)
)
@@ -231,9 +239,9 @@ class TooManyFunctionsSpec : Spek({
val configuredRule = TooManyFunctions(
TestConfig(
mapOf(
TooManyFunctions.THRESHOLD_IN_CLASSES to "1",
TooManyFunctions.THRESHOLD_IN_FILES to "1",
TooManyFunctions.IGNORE_OVERRIDDEN to "false"
THRESHOLD_IN_CLASSES to "1",
THRESHOLD_IN_FILES to "1",
IGNORE_OVERRIDDEN to "false"
)
)
)