diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt index 7a7a4ccce..d7ca5a134 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt @@ -1,36 +1,70 @@ package io.gitlab.arturbosch.detekt.api import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.diagnostics.DiagnosticUtils +import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getTextWithLocation import org.jetbrains.kotlin.psi.psiUtil.startOffset +import kotlin.reflect.memberProperties /** * @author Artur Bosch */ - -interface Finding { +interface Finding : Compactable, Describable, Reflective { val id: String + val location: Location + get() = entity.location + val entity: Entity + val metrics: List + val references: List +} + +interface Reflective { + fun findAttribute(name: String): Any? { + return this.javaClass.kotlin.memberProperties.find { it.name == name }?.get(this) + } +} + +interface Compactable { fun compact(): String } -class ThresholdedCodeSmell(id: String, location: Location, val value: Int, val threshold: Int) : CodeSmell(id, location) { +interface Describable { + val description: String +} + +class ThresholdedCodeSmell(id: String, entity: Entity, val value: Int, val threshold: Int) : CodeSmell(id, entity) { override fun compact(): String { return "$id - $value/$threshold - l/c${location.source} - ${location.text} - ${location.file}" } } -open class CodeSmell(override val id: String, val location: Location) : Finding { +open class CodeSmell(override val id: String, + override val entity: Entity, + override val metrics: List = listOf(), + override val references: List = listOf()) : Finding { + override val description: String = "" override fun compact(): String { - return "$id - l/c${location.source} - ${location.text} - ${location.file}" + return "$id - ${entity.compact()}" } } +data class Metric(val type: String, + val value: Int, + val threshold: Int, + val isDouble: Boolean) + data class Location(val source: SourceLocation, val text: TextLocation, val locationString: String, - val file: String) { + val file: String) : Compactable { + + override fun compact(): String { + return "L/C=$source - Chars=$text - $file" + } companion object { @@ -54,6 +88,42 @@ data class Location(val source: SourceLocation, private fun startLineAndColumn(element: PsiElement) = DiagnosticUtils.getLineAndColumnInPsiFile( element.containingFile, element.textRange) } +} + +data class Entity(val name: String, + val className: String, + val signature: String, + val location: Location) : Compactable { + + override fun compact(): String { + return "$name/$className - ${location.compact()}" + } + + companion object { + + fun from(startElement: PsiElement, endElementExclusively: PsiElement?): Entity { + val name = startElement.namedUnwrappedElement?.name ?: "Not found: ${startElement.text}" + val signature = startElement.text + val clazz = searchClass(startElement) + return Entity(name, clazz, signature, Location.from(startElement, endElementExclusively)) + } + + fun from(element: PsiElement): Entity { + val name = searchName(element) + val signature = element.text + val clazz = searchClass(element) + return Entity(name, clazz, signature, Location.from(element)) + } + + private fun searchName(element: PsiElement): String { + return element.namedUnwrappedElement?.name ?: "Not found: ${element.text}" + } + + private fun searchClass(element: PsiElement): String { + return element.getNonStrictParentOfType(KtClassOrObject::class.java)?.name ?: element.containingFile.name + } + + } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateMethod.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateMethod.kt index e24b31bbd..db994646b 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateMethod.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateMethod.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.CodeSmellRule import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtNamedFunction @@ -16,7 +16,7 @@ class CommentOverPrivateMethod(config: Config = Config.EMPTY) : CodeSmellRule("C val modifierList = function.modifierList if (modifierList != null && function.docComment != null) { if (modifierList.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - addFindings(CodeSmell(id, Location.from(function.docComment!!))) + addFindings(CodeSmell(id, Entity.from(function.docComment!!))) } } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateProperty.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateProperty.kt index 902924267..c6f52282c 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateProperty.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/CommentOverPrivateProperty.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.CodeSmellRule import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtProperty @@ -16,7 +16,7 @@ class CommentOverPrivateProperty(config: Config = Config.EMPTY) : CodeSmellRule( val modifierList = property.modifierList if (modifierList != null && property.docComment != null) { if (modifierList.hasModifier(KtTokens.PRIVATE_KEYWORD)) { - addFindings(CodeSmell(id, Location.from(property.docComment!!))) + addFindings(CodeSmell(id, Entity.from(property.docComment!!))) } } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/ComplexMethod.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/ComplexMethod.kt index 28a30a2e1..802291148 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/ComplexMethod.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/ComplexMethod.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmellThresholdRule import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.KastVisitor -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtIfExpression @@ -20,7 +20,7 @@ class ComplexMethod(config: Config = Config.EMPTY, threshold: Int = 10) : CodeSm override fun visitNamedFunction(function: KtNamedFunction) { val mcc = MccVisitor().visit(function) if (mcc > threshold) { - addFindings(ThresholdedCodeSmell(id, Location.from(function), mcc, threshold)) + addFindings(ThresholdedCodeSmell(id, Entity.from(function), mcc, threshold)) } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LargeClass.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LargeClass.kt index a71169aa5..34385b2c4 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LargeClass.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LargeClass.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.CodeSmellThresholdRule import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtClassOrObject @@ -38,7 +38,7 @@ class LargeClass(config: Config = Config.EMPTY, threshold: Int = 70) : CodeSmell incHead() // for class body super.visitClassOrObject(classOrObject) if (locStack.pop() > threshold) { - addFindings(CodeSmell(id, Location.from(classOrObject))) + addFindings(CodeSmell(id, Entity.from(classOrObject))) } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongMethod.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongMethod.kt index 3f81ffbcd..7134e38da 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongMethod.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongMethod.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.CodeSmellThresholdRule import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtNamedFunction @@ -16,7 +16,7 @@ class LongMethod(config: Config = Config.EMPTY, threshold: Int = 20) : CodeSmell val body: KtBlockExpression? = function.bodyExpression.asBlockExpression() body?.let { val size = body.statements.size - if (size > threshold) addFindings(CodeSmell(id, Location.from(function))) + if (size > threshold) addFindings(CodeSmell(id, Entity.from(function))) } super.visitNamedFunction(function) } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongParameterList.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongParameterList.kt index 37b4c700b..6d74e0f4d 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongParameterList.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/LongParameterList.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.CodeSmellThresholdRule import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import org.jetbrains.kotlin.psi.KtParameterList /** @@ -13,7 +13,7 @@ class LongParameterList(config: Config = Config.EMPTY, threshold: Int = 5) : Cod override fun visitParameterList(list: KtParameterList) { if (list.parameters.size > threshold) { - addFindings(CodeSmell(id, Location.from(list))) + addFindings(CodeSmell(id, Entity.from(list))) } } } \ No newline at end of file diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt index f1767814b..820740574 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NamingConventionViolation.kt @@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtNamedDeclaration @@ -23,13 +23,13 @@ class NamingConventionViolation(config: Config = Config.EMPTY) : Rule("NamingCon declaration.nameIdentifier?.parent?.javaClass?.let { val name = declaration.nameAsSafeName.asString() if (declaration is KtVariableDeclaration && !name.matches(variablePattern)) { - addFindings(CodeSmell(id, Location.from(declaration))) + addFindings(CodeSmell(id, Entity.from(declaration))) } if (declaration is KtNamedFunction && !name.matches(methodPattern)) { - addFindings(CodeSmell(id, Location.from(declaration))) + addFindings(CodeSmell(id, Entity.from(declaration))) } if (declaration is KtClassOrObject && !name.matches(classPattern)) { - addFindings(CodeSmell(id, Location.from(declaration))) + addFindings(CodeSmell(id, Entity.from(declaration))) } } super.visitNamedDeclaration(declaration) diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt index c3447d8d8..1f5194349 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt @@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtClassOrObject @@ -13,7 +13,7 @@ class NoDocOverPublicClass(config: Config = Config.EMPTY) : Rule("NoDocOverPubli override fun visitClassOrObject(classOrObject: KtClassOrObject) { if (classOrObject.isPublicNotOverriden()) { - addFindings(CodeSmell(id, Location.from(classOrObject, classOrObject.getBody()))) + addFindings(CodeSmell(id, Entity.from(classOrObject, classOrObject.getBody()))) } super.visitClassOrObject(classOrObject) } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt index 1fb1c5fbe..6d520cafe 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt @@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtNamedFunction @@ -26,6 +26,6 @@ class NoDocOverPublicMethod(config: Config = Config.EMPTY) : Rule("NoDocOverPubl } } - private fun methodHeaderLocation(function: KtNamedFunction) = Location.from(function, function.colon) + private fun methodHeaderLocation(function: KtNamedFunction) = Entity.from(function, function.colon) } \ No newline at end of file diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoElseInWhenExpression.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoElseInWhenExpression.kt index 827dd4e6a..7cd371c7f 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoElseInWhenExpression.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoElseInWhenExpression.kt @@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtWhenExpression @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.psi.KtWhenExpression class NoElseInWhenExpression(config: Config = Config.EMPTY) : Rule("NoElseInWhenExpression", Severity.Defect, config) { override fun visitWhenExpression(expression: KtWhenExpression) { - if (expression.elseExpression == null) addFindings(CodeSmell(id, Location.from(expression))) + if (expression.elseExpression == null) addFindings(CodeSmell(id, Entity.from(expression))) } } \ No newline at end of file diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalSemicolon.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalSemicolon.kt index dbbbd0adc..9bbd17639 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalSemicolon.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalSemicolon.kt @@ -8,7 +8,7 @@ import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.util.PsiTreeUtil import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.TokenRule import org.jetbrains.kotlin.psi.KtEnumEntry @@ -24,11 +24,11 @@ class OptionalSemicolon(config: Config = Config.EMPTY) : TokenRule("OptionalSemi val psi = node.psi if (psi.isNoErrorElement() && psi.isNotPartOfEnum() && psi.isNotPartOfString()) { if (psi.isDoubleSemicolon()) { - addFindings(CodeSmell(id, Location.from(psi))) + addFindings(CodeSmell(id, Entity.from(psi))) } else if (psi.isSemicolon()) { val nextLeaf = PsiTreeUtil.nextLeaf(psi) if (isSemicolonOrEOF(nextLeaf) || nextTokenHasSpaces(nextLeaf)) { - addFindings(CodeSmell(id, Location.from(psi))) + addFindings(CodeSmell(id, Entity.from(psi))) } } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalUnit.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalUnit.kt index f06e9b57a..024675f57 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalUnit.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/OptionalUnit.kt @@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtNamedFunction @@ -17,7 +17,7 @@ class OptionalUnit(config: Config = Config.EMPTY) : Rule("OptionalUnit", Severit val typeReference = function.typeReference typeReference?.typeElement?.text?.let { if (it == "Unit") { - addFindings(CodeSmell(id, Location.from(typeReference))) + addFindings(CodeSmell(id, Entity.from(typeReference))) } } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/WildcardImport.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/WildcardImport.kt index af885bea3..7f56f1679 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/WildcardImport.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/WildcardImport.kt @@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.gitlab.arturbosch.detekt.api.CodeSmell import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtImportDirective @@ -14,7 +14,7 @@ class WildcardImport(config: Config = Config.EMPTY) : Rule("WildcardImport", Sev override fun visitImportDirective(importDirective: KtImportDirective) { val import = importDirective.importPath?.pathStr if (import != null && import.contains("*")) { - addFindings(CodeSmell(id, Location.from(importDirective))) + addFindings(CodeSmell(id, Entity.from(importDirective))) } } }