Change finding model introducing entity and metric data classes

This commit is contained in:
ArtiSmarti
2016-10-21 23:17:52 +02:00
parent 010c44647d
commit 0f11f2939d
14 changed files with 105 additions and 35 deletions

View File

@@ -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<Metric>
val references: List<Entity>
}
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<Metric> = listOf(),
override val references: List<Entity> = 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
}
}
}

View File

@@ -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!!)))
}
}
}

View File

@@ -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!!)))
}
}
}

View File

@@ -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))
}
}

View File

@@ -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)))
}
}

View File

@@ -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)
}

View File

@@ -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)))
}
}
}

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)))
}
}

View File

@@ -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)))
}
}
}

View File

@@ -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)))
}
}
}

View File

@@ -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)))
}
}
}