mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
Change finding model introducing entity and metric data classes
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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!!)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!!)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user